triflux 10.3.4 → 10.7.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/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
package/hub/fullcycle.mjs
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
// hub/fullcycle.mjs — tfx-fullcycle runtime artifact/state helpers
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
statSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
|
+
import { ensureTfxDirs, TFX_FULLCYCLE_DIR, TFX_PLANS_DIR } from "./paths.mjs";
|
|
6
13
|
|
|
7
14
|
function safeResolve(baseDir, relativePath) {
|
|
8
15
|
const base = resolve(baseDir);
|
|
9
16
|
const target = resolve(join(baseDir, relativePath));
|
|
10
17
|
if (!target.startsWith(base)) {
|
|
11
|
-
throw new Error(
|
|
18
|
+
throw new Error("Invalid fullcycle path: path traversal detected");
|
|
12
19
|
}
|
|
13
20
|
return target;
|
|
14
21
|
}
|
|
@@ -17,9 +24,9 @@ function safeResolve(baseDir, relativePath) {
|
|
|
17
24
|
export function createFullcycleRunId(now = new Date()) {
|
|
18
25
|
return now
|
|
19
26
|
.toISOString()
|
|
20
|
-
.replace(/[:.]/g,
|
|
21
|
-
.replace(
|
|
22
|
-
.replace(
|
|
27
|
+
.replace(/[:.]/g, "-")
|
|
28
|
+
.replace("T", "_")
|
|
29
|
+
.replace("Z", "Z");
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
export function getFullcycleRunDir(runId, baseDir = process.cwd()) {
|
|
@@ -33,32 +40,41 @@ export function ensureFullcycleRunDir(runId, baseDir = process.cwd()) {
|
|
|
33
40
|
return dir;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
|
-
export function saveFullcycleArtifact(
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
export function saveFullcycleArtifact(
|
|
44
|
+
runId,
|
|
45
|
+
filename,
|
|
46
|
+
content,
|
|
47
|
+
baseDir = process.cwd(),
|
|
48
|
+
) {
|
|
49
|
+
if (!filename || typeof filename !== "string") {
|
|
50
|
+
throw new Error("Artifact filename is required");
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
const dir = ensureFullcycleRunDir(runId, baseDir);
|
|
42
54
|
const path = safeResolve(dir, filename);
|
|
43
|
-
writeFileSync(path, content,
|
|
55
|
+
writeFileSync(path, content, "utf8");
|
|
44
56
|
return path;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
|
-
export function readFullcycleArtifact(
|
|
59
|
+
export function readFullcycleArtifact(
|
|
60
|
+
runId,
|
|
61
|
+
filename,
|
|
62
|
+
baseDir = process.cwd(),
|
|
63
|
+
) {
|
|
48
64
|
const dir = getFullcycleRunDir(runId, baseDir);
|
|
49
65
|
const path = safeResolve(dir, filename);
|
|
50
66
|
if (!existsSync(path)) return null;
|
|
51
|
-
return readFileSync(path,
|
|
67
|
+
return readFileSync(path, "utf8");
|
|
52
68
|
}
|
|
53
69
|
|
|
54
70
|
export function writeFullcycleState(runId, state, baseDir = process.cwd()) {
|
|
55
|
-
const payload = typeof state ===
|
|
71
|
+
const payload = typeof state === "object" && state !== null ? state : {};
|
|
56
72
|
const serialized = JSON.stringify(payload, null, 2);
|
|
57
|
-
return saveFullcycleArtifact(runId,
|
|
73
|
+
return saveFullcycleArtifact(runId, "state.json", serialized, baseDir);
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
export function readFullcycleState(runId, baseDir = process.cwd()) {
|
|
61
|
-
const content = readFullcycleArtifact(runId,
|
|
77
|
+
const content = readFullcycleArtifact(runId, "state.json", baseDir);
|
|
62
78
|
if (!content) return null;
|
|
63
79
|
try {
|
|
64
80
|
return JSON.parse(content);
|
|
@@ -87,7 +103,7 @@ export function shouldStopQaLoop(failureHistory = [], maxRepeats = 3) {
|
|
|
87
103
|
if (!Array.isArray(failureHistory) || maxRepeats <= 1) return false;
|
|
88
104
|
|
|
89
105
|
const normalized = failureHistory
|
|
90
|
-
.map((entry) => String(entry ??
|
|
106
|
+
.map((entry) => String(entry ?? "").trim())
|
|
91
107
|
.filter(Boolean);
|
|
92
108
|
|
|
93
109
|
if (normalized.length < maxRepeats) return false;
|
package/hub/gemini-adapter.mjs
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
// hub/gemini-adapter.mjs — Gemini CLI 방어 계층
|
|
2
2
|
// codex-adapter.mjs와 동일 패턴, cli-adapter-base 공통 인터페이스 사용
|
|
3
3
|
|
|
4
|
-
import { mkdirSync } from
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { withRetry } from './workers/worker-utils.mjs';
|
|
9
|
-
import { whichCommandAsync } from './platform.mjs';
|
|
4
|
+
import { mkdirSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
10
7
|
import {
|
|
11
|
-
|
|
12
|
-
createResult,
|
|
13
|
-
appendWarnings,
|
|
8
|
+
executeWithCircuitBroker,
|
|
14
9
|
normalizePathForShell,
|
|
15
|
-
shellQuote,
|
|
16
10
|
runProcess,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
shellQuote,
|
|
12
|
+
} from "./cli-adapter-base.mjs";
|
|
13
|
+
import { whichCommandAsync } from "./platform.mjs";
|
|
20
14
|
|
|
21
15
|
// ── Gemini-specific stall inference ─────────────────────────────
|
|
22
16
|
|
|
23
17
|
function inferStallMode(stdout, stderr) {
|
|
24
18
|
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
25
|
-
if (/(rate.?limit|quota|resource.?exhaust|429)/u.test(text))
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
19
|
+
if (/(rate.?limit|quota|resource.?exhaust|429)/u.test(text))
|
|
20
|
+
return "rate_limited";
|
|
21
|
+
if (
|
|
22
|
+
/(unauthorized|forbidden|auth|login|token|credential|api.?key)/u.test(text)
|
|
23
|
+
)
|
|
24
|
+
return "auth_stall";
|
|
25
|
+
if (/\bmcp\b|playwright|tavily|brave|sequential|server/u.test(text))
|
|
26
|
+
return "mcp_stall";
|
|
27
|
+
return "timeout";
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
// ── Preflight ───────────────────────────────────────────────────
|
|
32
31
|
|
|
33
32
|
async function runPreflight(opts = {}) {
|
|
34
|
-
const geminiPath = await whichCommandAsync(
|
|
33
|
+
const geminiPath = await whichCommandAsync("gemini");
|
|
35
34
|
if (!geminiPath) {
|
|
36
35
|
return {
|
|
37
36
|
geminiPath: null,
|
|
38
|
-
warnings: [
|
|
37
|
+
warnings: [
|
|
38
|
+
"Gemini CLI not found. Install Gemini and ensure `gemini` is available on PATH.",
|
|
39
|
+
],
|
|
39
40
|
excludeMcpServers: [],
|
|
40
41
|
ok: false,
|
|
41
42
|
};
|
|
@@ -45,7 +46,7 @@ async function runPreflight(opts = {}) {
|
|
|
45
46
|
const excludeMcpServers = [];
|
|
46
47
|
|
|
47
48
|
for (const name of Array.isArray(opts.mcpServers) ? opts.mcpServers : []) {
|
|
48
|
-
const server = String(name ??
|
|
49
|
+
const server = String(name ?? "").trim();
|
|
49
50
|
if (!server) continue;
|
|
50
51
|
// Gemini MCP health는 best-effort: 실행 시점에 --allowed-mcp-server-names로 필터링
|
|
51
52
|
// 사전 probe는 수행하지 않음 (gemini가 자체적으로 graceful degrade)
|
|
@@ -57,26 +58,33 @@ async function runPreflight(opts = {}) {
|
|
|
57
58
|
// ── Command building ────────────────────────────────────────────
|
|
58
59
|
|
|
59
60
|
function buildGeminiCommand(prompt, resultFile, opts = {}) {
|
|
60
|
-
const parts = [
|
|
61
|
+
const parts = ["gemini"];
|
|
61
62
|
|
|
62
|
-
if (opts.model) parts.push(
|
|
63
|
-
parts.push(
|
|
63
|
+
if (opts.model) parts.push("--model", shellQuote(opts.model));
|
|
64
|
+
parts.push("--yolo");
|
|
64
65
|
|
|
65
|
-
const allowed = Array.isArray(opts.allowedMcpServers)
|
|
66
|
-
|
|
66
|
+
const allowed = Array.isArray(opts.allowedMcpServers)
|
|
67
|
+
? opts.allowedMcpServers
|
|
68
|
+
: [];
|
|
69
|
+
const excluded = Array.isArray(opts.excludeMcpServers)
|
|
70
|
+
? opts.excludeMcpServers
|
|
71
|
+
: [];
|
|
67
72
|
const filtered = allowed.filter((name) => !excluded.includes(name));
|
|
68
73
|
if (filtered.length) {
|
|
69
|
-
parts.push(
|
|
74
|
+
parts.push(
|
|
75
|
+
"--allowed-mcp-server-names",
|
|
76
|
+
...filtered.map((n) => shellQuote(n)),
|
|
77
|
+
);
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
parts.push(
|
|
73
|
-
parts.push(
|
|
80
|
+
parts.push("--prompt", shellQuote(prompt));
|
|
81
|
+
parts.push("--output-format", "text");
|
|
74
82
|
|
|
75
83
|
if (resultFile) {
|
|
76
|
-
return `${parts.join(
|
|
84
|
+
return `${parts.join(" ")} > ${shellQuote(normalizePathForShell(resultFile))} 2>${shellQuote(normalizePathForShell(resultFile + ".err"))}`;
|
|
77
85
|
}
|
|
78
86
|
|
|
79
|
-
return parts.join(
|
|
87
|
+
return parts.join(" ");
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
function buildAttempts(opts, preflight) {
|
|
@@ -84,20 +92,19 @@ function buildAttempts(opts, preflight) {
|
|
|
84
92
|
const base = {
|
|
85
93
|
timeout,
|
|
86
94
|
model: opts.model,
|
|
87
|
-
allowedMcpServers: Array.isArray(opts.mcpServers)
|
|
95
|
+
allowedMcpServers: Array.isArray(opts.mcpServers)
|
|
96
|
+
? [...opts.mcpServers]
|
|
97
|
+
: [],
|
|
88
98
|
excludeMcpServers: [...(preflight.excludeMcpServers || [])],
|
|
89
99
|
};
|
|
90
100
|
if (opts.retryOnFail === false) return [base];
|
|
91
|
-
return [
|
|
92
|
-
base,
|
|
93
|
-
{ ...base, timeout: timeout * 2, allowedMcpServers: [] },
|
|
94
|
-
];
|
|
101
|
+
return [base, { ...base, timeout: timeout * 2, allowedMcpServers: [] }];
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
// ── Public: buildExecArgs ───────────────────────────────────────
|
|
98
105
|
|
|
99
106
|
export function buildExecArgs(opts = {}) {
|
|
100
|
-
const prompt = typeof opts.prompt ===
|
|
107
|
+
const prompt = typeof opts.prompt === "string" ? opts.prompt : "";
|
|
101
108
|
return buildGeminiCommand(prompt, opts.resultFile || null, {
|
|
102
109
|
model: opts.model,
|
|
103
110
|
allowedMcpServers: opts.mcpServers,
|
|
@@ -107,74 +114,42 @@ export function buildExecArgs(opts = {}) {
|
|
|
107
114
|
// ── Execution ───────────────────────────────────────────────────
|
|
108
115
|
|
|
109
116
|
async function runGemini(prompt, workdir, preflight, attempt) {
|
|
110
|
-
const dir = join(tmpdir(),
|
|
117
|
+
const dir = join(tmpdir(), "triflux-gemini-exec");
|
|
111
118
|
mkdirSync(dir, { recursive: true });
|
|
112
|
-
const resultFile = join(
|
|
119
|
+
const resultFile = join(
|
|
120
|
+
dir,
|
|
121
|
+
`gemini-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`,
|
|
122
|
+
);
|
|
113
123
|
const command = buildGeminiCommand(prompt, resultFile, {
|
|
114
124
|
model: attempt.model,
|
|
115
125
|
allowedMcpServers: attempt.allowedMcpServers,
|
|
116
126
|
excludeMcpServers: attempt.excludeMcpServers,
|
|
117
127
|
});
|
|
118
|
-
return runProcess(command, workdir, attempt.timeout, {
|
|
128
|
+
return runProcess(command, workdir, attempt.timeout, {
|
|
129
|
+
resultFile,
|
|
130
|
+
inferStallMode,
|
|
131
|
+
});
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
// ── Public API ──────────────────────────────────────────────────
|
|
122
135
|
|
|
123
|
-
export function getCircuitState(
|
|
124
|
-
|
|
136
|
+
export async function getCircuitState() {
|
|
137
|
+
const brokerMod = await import("./account-broker.mjs");
|
|
138
|
+
if (!brokerMod.broker) return { state: "closed", failures: [] };
|
|
139
|
+
const snap = brokerMod.broker
|
|
140
|
+
.snapshot()
|
|
141
|
+
.filter((a) => a.provider === "gemini");
|
|
142
|
+
return snap.length
|
|
143
|
+
? { state: snap[0].circuitState, accounts: snap }
|
|
144
|
+
: { state: "closed", failures: [] };
|
|
125
145
|
}
|
|
126
146
|
|
|
127
|
-
export
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
breaker.clearTrial();
|
|
136
|
-
breaker.recordFailure(entry.halfOpen);
|
|
137
|
-
return createResult(false, {
|
|
138
|
-
stderr: appendWarnings('', preflight.warnings),
|
|
139
|
-
fellBack: opts.fallbackToClaude !== false,
|
|
140
|
-
failureMode: 'crash',
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const attempts = buildAttempts(opts, preflight);
|
|
145
|
-
let attemptIndex = 0;
|
|
146
|
-
let lastResult = createResult(false);
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
lastResult = await withRetry(async () => {
|
|
150
|
-
const result = await runGemini(opts.prompt || '', opts.workdir || process.cwd(), preflight, attempts[attemptIndex]);
|
|
151
|
-
const current = { ...result, stderr: appendWarnings(result.stderr, preflight.warnings), retried: attemptIndex > 0 };
|
|
152
|
-
const canRetry = !current.ok && attemptIndex < attempts.length - 1;
|
|
153
|
-
attemptIndex += 1;
|
|
154
|
-
if (!canRetry) return current;
|
|
155
|
-
const error = new Error('retry');
|
|
156
|
-
error.retryable = true;
|
|
157
|
-
error.result = current;
|
|
158
|
-
throw error;
|
|
159
|
-
}, {
|
|
160
|
-
maxAttempts: attempts.length,
|
|
161
|
-
baseDelayMs: 250,
|
|
162
|
-
maxDelayMs: 750,
|
|
163
|
-
shouldRetry: (error) => error?.retryable === true,
|
|
164
|
-
});
|
|
165
|
-
} catch (error) {
|
|
166
|
-
lastResult = error?.result || createResult(false, { stderr: String(error?.message || error) });
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (lastResult.ok) {
|
|
170
|
-
breaker.reset();
|
|
171
|
-
return lastResult;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
breaker.recordFailure(entry.halfOpen);
|
|
175
|
-
return {
|
|
176
|
-
...lastResult,
|
|
177
|
-
retried: attempts.length > 1,
|
|
178
|
-
fellBack: opts.fallbackToClaude !== false,
|
|
179
|
-
};
|
|
147
|
+
export function execute(opts = {}) {
|
|
148
|
+
return executeWithCircuitBroker({
|
|
149
|
+
provider: "gemini",
|
|
150
|
+
runFn: runGemini,
|
|
151
|
+
preflightFn: (o) => runPreflight({ mcpServers: o.mcpServers }),
|
|
152
|
+
buildAttemptsFn: buildAttempts,
|
|
153
|
+
opts,
|
|
154
|
+
});
|
|
180
155
|
}
|
package/hub/hitl.mjs
CHANGED
|
@@ -7,20 +7,34 @@
|
|
|
7
7
|
* @param {object} router — createRouter() 반환 객체
|
|
8
8
|
*/
|
|
9
9
|
export function createHitlManager(store, router = null) {
|
|
10
|
-
function forwardHumanResponse({
|
|
10
|
+
function forwardHumanResponse({
|
|
11
|
+
requesterAgent,
|
|
12
|
+
requestId,
|
|
13
|
+
action,
|
|
14
|
+
content,
|
|
15
|
+
submittedBy,
|
|
16
|
+
correlationId,
|
|
17
|
+
traceId,
|
|
18
|
+
priority,
|
|
19
|
+
}) {
|
|
11
20
|
if (!router?.handlePublish) {
|
|
12
|
-
throw new Error(
|
|
21
|
+
throw new Error("router.handlePublish is required for HITL forwarding");
|
|
13
22
|
}
|
|
14
23
|
return router.handlePublish({
|
|
15
|
-
from:
|
|
24
|
+
from: "hub:hitl",
|
|
16
25
|
to: requesterAgent,
|
|
17
|
-
topic:
|
|
26
|
+
topic: "human.response",
|
|
18
27
|
priority,
|
|
19
28
|
ttl_ms: 300000,
|
|
20
|
-
payload: {
|
|
29
|
+
payload: {
|
|
30
|
+
request_id: requestId,
|
|
31
|
+
action,
|
|
32
|
+
content,
|
|
33
|
+
submitted_by: submittedBy,
|
|
34
|
+
},
|
|
21
35
|
correlation_id: correlationId,
|
|
22
36
|
trace_id: traceId,
|
|
23
|
-
message_type:
|
|
37
|
+
message_type: "human_response",
|
|
24
38
|
});
|
|
25
39
|
}
|
|
26
40
|
|
|
@@ -30,24 +44,41 @@ export function createHitlManager(store, router = null) {
|
|
|
30
44
|
* 터미널에 알림 출력 후 pending 상태로 저장
|
|
31
45
|
*/
|
|
32
46
|
requestHumanInput({
|
|
33
|
-
requester_agent,
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
requester_agent,
|
|
48
|
+
kind,
|
|
49
|
+
prompt,
|
|
50
|
+
requested_schema = {},
|
|
51
|
+
deadline_ms,
|
|
52
|
+
default_action,
|
|
53
|
+
channel_preference = "terminal",
|
|
54
|
+
correlation_id,
|
|
55
|
+
trace_id,
|
|
36
56
|
}) {
|
|
37
57
|
const result = store.insertHumanRequest({
|
|
38
|
-
requester_agent,
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
requester_agent,
|
|
59
|
+
kind,
|
|
60
|
+
prompt,
|
|
61
|
+
requested_schema,
|
|
62
|
+
deadline_ms,
|
|
63
|
+
default_action,
|
|
64
|
+
correlation_id,
|
|
65
|
+
trace_id,
|
|
41
66
|
});
|
|
42
67
|
|
|
43
68
|
// 터미널 알림 (stderr — stdout은 MCP 용)
|
|
44
|
-
const kindLabel = {
|
|
69
|
+
const kindLabel = {
|
|
70
|
+
captcha: "CAPTCHA",
|
|
71
|
+
approval: "승인",
|
|
72
|
+
credential: "자격증명",
|
|
73
|
+
choice: "선택",
|
|
74
|
+
text: "텍스트",
|
|
75
|
+
};
|
|
45
76
|
process.stderr.write(
|
|
46
77
|
`\n[tfx-hub] 사용자 입력 요청 (${kindLabel[kind] || kind})\n` +
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
` 요청자: ${requester_agent}\n` +
|
|
79
|
+
` 내용: ${prompt}\n` +
|
|
80
|
+
` ID: ${result.request_id}\n` +
|
|
81
|
+
` 제한: ${Math.round(deadline_ms / 1000)}초\n\n`,
|
|
51
82
|
);
|
|
52
83
|
|
|
53
84
|
return { ok: true, data: result };
|
|
@@ -57,21 +88,45 @@ export function createHitlManager(store, router = null) {
|
|
|
57
88
|
* 사용자 입력 응답 제출
|
|
58
89
|
* 유효성 검증 → 상태 업데이트 → 요청자에게 응답 메시지 전달
|
|
59
90
|
*/
|
|
60
|
-
submitHumanInput({
|
|
91
|
+
submitHumanInput({
|
|
92
|
+
request_id,
|
|
93
|
+
action,
|
|
94
|
+
content = null,
|
|
95
|
+
submitted_by = "human",
|
|
96
|
+
}) {
|
|
61
97
|
// 요청 조회
|
|
62
98
|
const hr = store.getHumanRequest(request_id);
|
|
63
99
|
if (!hr) {
|
|
64
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
error: { code: "NOT_FOUND", message: `요청 없음: ${request_id}` },
|
|
103
|
+
};
|
|
65
104
|
}
|
|
66
|
-
if (hr.state !==
|
|
67
|
-
return {
|
|
105
|
+
if (hr.state !== "pending") {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
error: {
|
|
109
|
+
code: "ALREADY_HANDLED",
|
|
110
|
+
message: `이미 처리됨: ${hr.state}`,
|
|
111
|
+
},
|
|
112
|
+
};
|
|
68
113
|
}
|
|
69
114
|
|
|
70
115
|
// 상태 매핑
|
|
71
|
-
const stateMap = {
|
|
116
|
+
const stateMap = {
|
|
117
|
+
accept: "accepted",
|
|
118
|
+
decline: "declined",
|
|
119
|
+
cancel: "cancelled",
|
|
120
|
+
};
|
|
72
121
|
const newState = stateMap[action];
|
|
73
122
|
if (!newState) {
|
|
74
|
-
return {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
error: {
|
|
126
|
+
code: "INVALID_ACTION",
|
|
127
|
+
message: `잘못된 action: ${action}`,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
75
130
|
}
|
|
76
131
|
|
|
77
132
|
// DB 업데이트
|
|
@@ -79,7 +134,7 @@ export function createHitlManager(store, router = null) {
|
|
|
79
134
|
|
|
80
135
|
// 요청자에게 응답 메시지 전달
|
|
81
136
|
let forwardedMessageId = null;
|
|
82
|
-
if (action ===
|
|
137
|
+
if (action === "accept" || action === "decline") {
|
|
83
138
|
const published = forwardHumanResponse({
|
|
84
139
|
requesterAgent: hr.requester_agent,
|
|
85
140
|
requestId: request_id,
|
|
@@ -95,7 +150,11 @@ export function createHitlManager(store, router = null) {
|
|
|
95
150
|
|
|
96
151
|
return {
|
|
97
152
|
ok: true,
|
|
98
|
-
data: {
|
|
153
|
+
data: {
|
|
154
|
+
request_id,
|
|
155
|
+
new_state: newState,
|
|
156
|
+
forwarded_message_id: forwardedMessageId,
|
|
157
|
+
},
|
|
99
158
|
};
|
|
100
159
|
},
|
|
101
160
|
|
|
@@ -106,19 +165,19 @@ export function createHitlManager(store, router = null) {
|
|
|
106
165
|
checkTimeouts() {
|
|
107
166
|
const pending = store.getPendingHumanRequests();
|
|
108
167
|
const now = Date.now();
|
|
109
|
-
const expired = pending.filter(hr => hr.deadline_ms <= now);
|
|
168
|
+
const expired = pending.filter((hr) => hr.deadline_ms <= now);
|
|
110
169
|
if (!expired.length) return 0;
|
|
111
170
|
|
|
112
171
|
const expireRequests = () => {
|
|
113
172
|
for (const hr of expired) {
|
|
114
|
-
store.updateHumanRequest(hr.request_id,
|
|
115
|
-
if (hr.default_action ===
|
|
173
|
+
store.updateHumanRequest(hr.request_id, "timed_out", null);
|
|
174
|
+
if (hr.default_action === "timeout_continue") {
|
|
116
175
|
forwardHumanResponse({
|
|
117
176
|
requesterAgent: hr.requester_agent,
|
|
118
177
|
requestId: hr.request_id,
|
|
119
|
-
action:
|
|
178
|
+
action: "timeout_continue",
|
|
120
179
|
content: null,
|
|
121
|
-
submittedBy:
|
|
180
|
+
submittedBy: "system",
|
|
122
181
|
correlationId: hr.correlation_id,
|
|
123
182
|
traceId: hr.trace_id,
|
|
124
183
|
priority: 5,
|