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/team/staleState.mjs
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
// hub/team/staleState.mjs
|
|
2
2
|
// .omc/state 아래에 남은 stale team 상태를 탐지/정리한다.
|
|
3
3
|
|
|
4
|
-
import { existsSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync } from "node:fs";
|
|
5
4
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
rmSync,
|
|
10
|
+
statSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
} from "node:fs";
|
|
7
13
|
import { homedir } from "node:os";
|
|
8
|
-
|
|
9
|
-
import { forceCleanupTeam } from "./nativeProxy.mjs";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
15
|
import { isPidAlive } from "../lib/process-utils.mjs";
|
|
16
|
+
import { forceCleanupTeam } from "./nativeProxy.mjs";
|
|
11
17
|
|
|
12
18
|
export const TEAM_STATE_FILE_NAME = "team-state.json";
|
|
13
19
|
export const STALE_TEAM_MAX_AGE_MS = 60 * 60 * 1000;
|
|
@@ -99,7 +105,9 @@ function normalizeProcessEntries(processEntries = []) {
|
|
|
99
105
|
|
|
100
106
|
return processEntries.map((entry) => ({
|
|
101
107
|
pid: Number(entry?.pid ?? entry?.ProcessId ?? 0),
|
|
102
|
-
command: String(
|
|
108
|
+
command: String(
|
|
109
|
+
entry?.command ?? entry?.CommandLine ?? entry?.Name ?? "",
|
|
110
|
+
).toLowerCase(),
|
|
103
111
|
}));
|
|
104
112
|
}
|
|
105
113
|
|
|
@@ -166,9 +174,11 @@ function resolveLiveness(state, sessionId, liveSessionNames, processEntries) {
|
|
|
166
174
|
|
|
167
175
|
const processTokens = findProcessTokens(state, sessionId);
|
|
168
176
|
if (processTokens.length > 0) {
|
|
169
|
-
const matched = processEntries.find(
|
|
170
|
-
entry
|
|
171
|
-
|
|
177
|
+
const matched = processEntries.find(
|
|
178
|
+
(entry) =>
|
|
179
|
+
entry.pid > 0 &&
|
|
180
|
+
processTokens.some((token) => entry.command.includes(token)),
|
|
181
|
+
);
|
|
172
182
|
if (matched) {
|
|
173
183
|
return { active: true, reason: `command:${matched.pid}` };
|
|
174
184
|
}
|
|
@@ -258,14 +268,23 @@ export function findNearestOmcStateDir(startDir = process.cwd()) {
|
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
export function inspectStaleOmcTeams(options = {}) {
|
|
261
|
-
const stateRoot =
|
|
271
|
+
const stateRoot =
|
|
272
|
+
options.stateRoot !== undefined
|
|
273
|
+
? options.stateRoot
|
|
274
|
+
: findNearestOmcStateDir(options.startDir || process.cwd());
|
|
262
275
|
const requestedTeamsRoot = options.teamsRoot || CLAUDE_TEAMS_ROOT;
|
|
263
|
-
const teamsRoot = safeStat(requestedTeamsRoot)?.isDirectory()
|
|
276
|
+
const teamsRoot = safeStat(requestedTeamsRoot)?.isDirectory()
|
|
277
|
+
? requestedTeamsRoot
|
|
278
|
+
: null;
|
|
264
279
|
|
|
265
280
|
const liveSessionNames = new Set(options.liveSessionNames || []);
|
|
266
|
-
const processEntries = normalizeProcessEntries(
|
|
281
|
+
const processEntries = normalizeProcessEntries(
|
|
282
|
+
options.processEntries || readProcessEntries(),
|
|
283
|
+
);
|
|
267
284
|
const nowMs = Number.isFinite(options.nowMs) ? options.nowMs : Date.now();
|
|
268
|
-
const maxAgeMs = Number.isFinite(options.maxAgeMs)
|
|
285
|
+
const maxAgeMs = Number.isFinite(options.maxAgeMs)
|
|
286
|
+
? options.maxAgeMs
|
|
287
|
+
: STALE_TEAM_MAX_AGE_MS;
|
|
269
288
|
const targets = [
|
|
270
289
|
...(stateRoot ? collectTeamStateTargets(stateRoot) : []),
|
|
271
290
|
...collectClaudeTeamTargets(teamsRoot),
|
|
@@ -291,25 +310,48 @@ export function inspectStaleOmcTeams(options = {}) {
|
|
|
291
310
|
|
|
292
311
|
const fileStat = safeStat(target.stateFile);
|
|
293
312
|
const teamDirStat = target.teamDir ? safeStat(target.teamDir) : null;
|
|
294
|
-
const createdAtMs = Number.isFinite(state?.createdAt)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
??
|
|
299
|
-
??
|
|
300
|
-
??
|
|
313
|
+
const createdAtMs = Number.isFinite(state?.createdAt)
|
|
314
|
+
? state.createdAt
|
|
315
|
+
: null;
|
|
316
|
+
const startedAtMs =
|
|
317
|
+
parseStartedAtMs(state?.started_at) ??
|
|
318
|
+
parseStartedAtMs(state?.startedAt) ??
|
|
319
|
+
createdAtMs ??
|
|
320
|
+
fileStat?.mtimeMs ??
|
|
321
|
+
teamDirStat?.mtimeMs ??
|
|
322
|
+
null;
|
|
301
323
|
const ageMs = startedAtMs == null ? null : Math.max(0, nowMs - startedAtMs);
|
|
302
|
-
const teamName =
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
324
|
+
const teamName =
|
|
325
|
+
state?.teamName ||
|
|
326
|
+
state?.team_name ||
|
|
327
|
+
state?.native?.teamName ||
|
|
328
|
+
state?.name ||
|
|
329
|
+
target.teamName ||
|
|
330
|
+
null;
|
|
331
|
+
const livenessState =
|
|
332
|
+
target.scope === "claude_team"
|
|
333
|
+
? {
|
|
334
|
+
...(state || {}),
|
|
335
|
+
name: teamName,
|
|
336
|
+
teamName,
|
|
337
|
+
sessionName:
|
|
338
|
+
state?.leadSessionId ||
|
|
339
|
+
state?.lead_session_id ||
|
|
340
|
+
state?.sessionName ||
|
|
341
|
+
target.sessionId,
|
|
342
|
+
sessionId:
|
|
343
|
+
state?.leadSessionId ||
|
|
344
|
+
state?.lead_session_id ||
|
|
345
|
+
state?.sessionId ||
|
|
346
|
+
target.sessionId,
|
|
347
|
+
}
|
|
348
|
+
: state;
|
|
349
|
+
const liveness = resolveLiveness(
|
|
350
|
+
livenessState,
|
|
351
|
+
target.sessionId,
|
|
352
|
+
liveSessionNames,
|
|
353
|
+
processEntries,
|
|
354
|
+
);
|
|
313
355
|
const stale = ageMs != null && ageMs >= maxAgeMs && !liveness.active;
|
|
314
356
|
|
|
315
357
|
entries.push({
|
package/hub/team/swarm-locks.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Lock state is kept in-memory (single-process hypervisor) with optional
|
|
4
4
|
// JSON persistence to .triflux/swarm-locks.json for crash recovery.
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { dirname,
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, relative, resolve } from "node:path";
|
|
8
8
|
|
|
9
9
|
const LOCK_TTL_MS = 10 * 60_000; // 10 minutes default TTL
|
|
10
10
|
|
|
@@ -17,11 +17,7 @@ const LOCK_TTL_MS = 10 * 60_000; // 10 minutes default TTL
|
|
|
17
17
|
* @returns {SwarmLockManager}
|
|
18
18
|
*/
|
|
19
19
|
export function createSwarmLocks(opts = {}) {
|
|
20
|
-
const {
|
|
21
|
-
repoRoot = process.cwd(),
|
|
22
|
-
persistPath,
|
|
23
|
-
ttlMs = LOCK_TTL_MS,
|
|
24
|
-
} = opts;
|
|
20
|
+
const { repoRoot = process.cwd(), persistPath, ttlMs = LOCK_TTL_MS } = opts;
|
|
25
21
|
|
|
26
22
|
/** @type {Map<string, LockEntry>} normalized relative path → lock */
|
|
27
23
|
const locks = new Map();
|
|
@@ -30,7 +26,7 @@ export function createSwarmLocks(opts = {}) {
|
|
|
30
26
|
|
|
31
27
|
function normalizePath(filePath) {
|
|
32
28
|
const abs = resolve(repoRoot, filePath);
|
|
33
|
-
return relative(repoRoot, abs).replace(/\\/g,
|
|
29
|
+
return relative(repoRoot, abs).replace(/\\/g, "/");
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
function now() {
|
|
@@ -56,21 +52,25 @@ export function createSwarmLocks(opts = {}) {
|
|
|
56
52
|
const data = Object.fromEntries(
|
|
57
53
|
[...locks].map(([k, v]) => [k, { ...v }]),
|
|
58
54
|
);
|
|
59
|
-
writeFileSync(persistPath, JSON.stringify(data, null, 2),
|
|
60
|
-
} catch {
|
|
55
|
+
writeFileSync(persistPath, JSON.stringify(data, null, 2), "utf8");
|
|
56
|
+
} catch {
|
|
57
|
+
/* best-effort */
|
|
58
|
+
}
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
function restore() {
|
|
64
62
|
if (!persistPath || !existsSync(persistPath)) return;
|
|
65
63
|
try {
|
|
66
|
-
const data = JSON.parse(readFileSync(persistPath,
|
|
64
|
+
const data = JSON.parse(readFileSync(persistPath, "utf8"));
|
|
67
65
|
const ts = now();
|
|
68
66
|
for (const [path, entry] of Object.entries(data)) {
|
|
69
67
|
if (ts - entry.acquiredAt <= ttlMs) {
|
|
70
68
|
locks.set(path, entry);
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
|
-
} catch {
|
|
71
|
+
} catch {
|
|
72
|
+
/* corrupted file — start fresh */
|
|
73
|
+
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// restore on creation
|
|
@@ -199,6 +199,8 @@ export function createSwarmLocks(opts = {}) {
|
|
|
199
199
|
validateChanges,
|
|
200
200
|
snapshot,
|
|
201
201
|
releaseAll,
|
|
202
|
-
get size() {
|
|
202
|
+
get size() {
|
|
203
|
+
return locks.size;
|
|
204
|
+
},
|
|
203
205
|
});
|
|
204
206
|
}
|
|
@@ -14,17 +14,17 @@
|
|
|
14
14
|
// - prompt: |
|
|
15
15
|
// multi-line prompt text
|
|
16
16
|
|
|
17
|
-
import { readFileSync } from
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
18
|
|
|
19
19
|
/** Shard schema defaults */
|
|
20
20
|
const SHARD_DEFAULTS = Object.freeze({
|
|
21
|
-
agent:
|
|
21
|
+
agent: "codex",
|
|
22
22
|
files: [],
|
|
23
23
|
mcp: [],
|
|
24
24
|
depends: [],
|
|
25
25
|
critical: false,
|
|
26
|
-
prompt:
|
|
27
|
-
host:
|
|
26
|
+
prompt: "",
|
|
27
|
+
host: "",
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -41,7 +41,7 @@ export function parseShards(content) {
|
|
|
41
41
|
|
|
42
42
|
function flushPrompt() {
|
|
43
43
|
if (current && promptLines.length > 0) {
|
|
44
|
-
current.prompt = promptLines.join(
|
|
44
|
+
current.prompt = promptLines.join("\n").trim();
|
|
45
45
|
promptLines = [];
|
|
46
46
|
}
|
|
47
47
|
inPrompt = false;
|
|
@@ -91,26 +91,35 @@ export function parseShards(content) {
|
|
|
91
91
|
const value = rawValue.trim();
|
|
92
92
|
|
|
93
93
|
switch (key.toLowerCase()) {
|
|
94
|
-
case
|
|
94
|
+
case "agent":
|
|
95
95
|
current.agent = value.toLowerCase();
|
|
96
96
|
break;
|
|
97
|
-
case
|
|
98
|
-
current.files = value
|
|
97
|
+
case "files":
|
|
98
|
+
current.files = value
|
|
99
|
+
.split(/,\s*/)
|
|
100
|
+
.map((f) => f.trim())
|
|
101
|
+
.filter(Boolean);
|
|
99
102
|
break;
|
|
100
|
-
case
|
|
101
|
-
current.mcp = value
|
|
103
|
+
case "mcp":
|
|
104
|
+
current.mcp = value
|
|
105
|
+
.split(/,\s*/)
|
|
106
|
+
.map((s) => s.trim())
|
|
107
|
+
.filter(Boolean);
|
|
102
108
|
break;
|
|
103
|
-
case
|
|
104
|
-
current.depends = value
|
|
109
|
+
case "depends":
|
|
110
|
+
current.depends = value
|
|
111
|
+
.split(/,\s*/)
|
|
112
|
+
.map((d) => d.trim())
|
|
113
|
+
.filter(Boolean);
|
|
105
114
|
break;
|
|
106
|
-
case
|
|
115
|
+
case "critical":
|
|
107
116
|
current.critical = /^(true|yes|1)$/i.test(value);
|
|
108
117
|
break;
|
|
109
|
-
case
|
|
118
|
+
case "host":
|
|
110
119
|
current.host = value;
|
|
111
120
|
break;
|
|
112
|
-
case
|
|
113
|
-
if (value && !value.startsWith(
|
|
121
|
+
case "prompt":
|
|
122
|
+
if (value && !value.startsWith("|")) {
|
|
114
123
|
current.prompt = value;
|
|
115
124
|
} else {
|
|
116
125
|
inPrompt = true;
|
|
@@ -178,8 +187,8 @@ export function buildMcpManifest(shards) {
|
|
|
178
187
|
*/
|
|
179
188
|
export function computeMergeOrder(shards) {
|
|
180
189
|
const nameSet = new Set(shards.map((s) => s.name));
|
|
181
|
-
const adj = new Map();
|
|
182
|
-
const inDeg = new Map();
|
|
190
|
+
const adj = new Map(); // name → [dependents]
|
|
191
|
+
const inDeg = new Map(); // name → number
|
|
183
192
|
|
|
184
193
|
for (const shard of shards) {
|
|
185
194
|
adj.set(shard.name, []);
|
|
@@ -234,11 +243,13 @@ export function computeMergeOrder(shards) {
|
|
|
234
243
|
* @returns {SwarmPlan}
|
|
235
244
|
*/
|
|
236
245
|
export function planSwarm(prdPath, opts = {}) {
|
|
237
|
-
const content = opts.content || readFileSync(prdPath,
|
|
246
|
+
const content = opts.content || readFileSync(prdPath, "utf8");
|
|
238
247
|
const shards = parseShards(content);
|
|
239
248
|
|
|
240
249
|
if (shards.length === 0) {
|
|
241
|
-
throw new Error(
|
|
250
|
+
throw new Error(
|
|
251
|
+
'No shards found in PRD. Expected "## Shard: <name>" sections.',
|
|
252
|
+
);
|
|
242
253
|
}
|
|
243
254
|
|
|
244
255
|
const { leaseMap, conflicts } = buildFileLeaseMap(shards);
|
|
@@ -246,7 +257,7 @@ export function planSwarm(prdPath, opts = {}) {
|
|
|
246
257
|
const { order: mergeOrder, cycles } = computeMergeOrder(shards);
|
|
247
258
|
|
|
248
259
|
if (cycles.length > 0) {
|
|
249
|
-
throw new Error(`Dependency cycle detected: ${cycles[0].join(
|
|
260
|
+
throw new Error(`Dependency cycle detected: ${cycles[0].join(" → ")}`);
|
|
250
261
|
}
|
|
251
262
|
|
|
252
263
|
return Object.freeze({
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// For critical shards: launches primary + verifier sessions, compares results,
|
|
3
3
|
// applies conservative adoption (fewer changes wins) or HITL fallback.
|
|
4
4
|
|
|
5
|
-
import { execFile } from
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Compare two shard results and decide which to accept.
|
|
@@ -21,14 +21,23 @@ export async function reconcile(primaryResult, verifierResult, opts = {}) {
|
|
|
21
21
|
const { rootDir = process.cwd(), maxDivergenceFiles = 5 } = opts;
|
|
22
22
|
|
|
23
23
|
// If either failed, pick the one that succeeded
|
|
24
|
-
if (
|
|
25
|
-
|
|
24
|
+
if (
|
|
25
|
+
primaryResult.status !== "completed" &&
|
|
26
|
+
verifierResult.status === "completed"
|
|
27
|
+
) {
|
|
28
|
+
return decision("verifier", "primary_failed", verifierResult);
|
|
26
29
|
}
|
|
27
|
-
if (
|
|
28
|
-
|
|
30
|
+
if (
|
|
31
|
+
verifierResult.status !== "completed" &&
|
|
32
|
+
primaryResult.status === "completed"
|
|
33
|
+
) {
|
|
34
|
+
return decision("primary", "verifier_failed", primaryResult);
|
|
29
35
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
36
|
+
if (
|
|
37
|
+
primaryResult.status !== "completed" &&
|
|
38
|
+
verifierResult.status !== "completed"
|
|
39
|
+
) {
|
|
40
|
+
return decision("none", "both_failed", null);
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
// Both completed — compare diffs
|
|
@@ -37,29 +46,37 @@ export async function reconcile(primaryResult, verifierResult, opts = {}) {
|
|
|
37
46
|
|
|
38
47
|
// Identical diffs → accept primary (no divergence)
|
|
39
48
|
if (primaryDiff.hash === verifierDiff.hash) {
|
|
40
|
-
return decision(
|
|
49
|
+
return decision("primary", "identical", primaryResult);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
// Compute divergence
|
|
44
|
-
const divergence = Math.abs(
|
|
53
|
+
const divergence = Math.abs(
|
|
54
|
+
primaryDiff.filesChanged - verifierDiff.filesChanged,
|
|
55
|
+
);
|
|
45
56
|
|
|
46
57
|
// High divergence → HITL
|
|
47
58
|
if (divergence > maxDivergenceFiles) {
|
|
48
59
|
return {
|
|
49
|
-
selected:
|
|
60
|
+
selected: "hitl",
|
|
50
61
|
reason: `divergence_too_high (${divergence} files differ)`,
|
|
51
62
|
result: null,
|
|
52
63
|
requiresManualReview: true,
|
|
53
|
-
primary: {
|
|
54
|
-
|
|
64
|
+
primary: {
|
|
65
|
+
filesChanged: primaryDiff.filesChanged,
|
|
66
|
+
linesChanged: primaryDiff.linesChanged,
|
|
67
|
+
},
|
|
68
|
+
verifier: {
|
|
69
|
+
filesChanged: verifierDiff.filesChanged,
|
|
70
|
+
linesChanged: verifierDiff.linesChanged,
|
|
71
|
+
},
|
|
55
72
|
};
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
// Conservative adoption: fewer changes wins
|
|
59
76
|
if (primaryDiff.linesChanged <= verifierDiff.linesChanged) {
|
|
60
|
-
return decision(
|
|
77
|
+
return decision("primary", "conservative_adoption", primaryResult);
|
|
61
78
|
}
|
|
62
|
-
return decision(
|
|
79
|
+
return decision("verifier", "conservative_adoption", verifierResult);
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
function decision(selected, reason, result) {
|
|
@@ -67,7 +84,7 @@ function decision(selected, reason, result) {
|
|
|
67
84
|
selected,
|
|
68
85
|
reason,
|
|
69
86
|
result,
|
|
70
|
-
requiresManualReview: selected ===
|
|
87
|
+
requiresManualReview: selected === "hitl" || selected === "none",
|
|
71
88
|
primary: null,
|
|
72
89
|
verifier: null,
|
|
73
90
|
};
|
|
@@ -82,8 +99,11 @@ function decision(selected, reason, result) {
|
|
|
82
99
|
*/
|
|
83
100
|
async function getDiffStat(branch, cwd) {
|
|
84
101
|
try {
|
|
85
|
-
const stat = await gitExec(
|
|
86
|
-
|
|
102
|
+
const stat = await gitExec(
|
|
103
|
+
["diff", "--stat", "--numstat", `${branch}~1..${branch}`],
|
|
104
|
+
cwd,
|
|
105
|
+
);
|
|
106
|
+
const lines = stat.split("\n").filter(Boolean);
|
|
87
107
|
let filesChanged = 0;
|
|
88
108
|
let linesChanged = 0;
|
|
89
109
|
|
|
@@ -96,20 +116,25 @@ async function getDiffStat(branch, cwd) {
|
|
|
96
116
|
}
|
|
97
117
|
|
|
98
118
|
// Get tree hash for identity comparison
|
|
99
|
-
const hash = await gitExec([
|
|
119
|
+
const hash = await gitExec(["rev-parse", `${branch}^{tree}`], cwd);
|
|
100
120
|
|
|
101
121
|
return { filesChanged, linesChanged, hash: hash.trim() };
|
|
102
122
|
} catch {
|
|
103
|
-
return { filesChanged: 0, linesChanged: 0, hash:
|
|
123
|
+
return { filesChanged: 0, linesChanged: 0, hash: "" };
|
|
104
124
|
}
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
function gitExec(args, cwd) {
|
|
108
128
|
return new Promise((res, rej) => {
|
|
109
|
-
execFile(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
execFile(
|
|
130
|
+
"git",
|
|
131
|
+
args,
|
|
132
|
+
{ cwd, windowsHide: true, timeout: 15_000 },
|
|
133
|
+
(err, stdout) => {
|
|
134
|
+
if (err) rej(err);
|
|
135
|
+
else res(stdout);
|
|
136
|
+
},
|
|
137
|
+
);
|
|
113
138
|
});
|
|
114
139
|
}
|
|
115
140
|
|