Files
oh-my-claudecode/bridge/runtime-cli.cjs
Codex Review 2995bab5e2 chore(release): bump version to v4.13.1
Release includes:
- feat(team): cursor-agent as 4th tmux worker type (#2736)
- fix(keyword-detector): stop false-positive autopilot on "autonomous" (#2739)
- fix(self-improve): scope artifacts by topic for safer resumes (#2732)

Constraint: 4 version files must remain in sync (package.json, plugin.json, marketplace.json, package-lock.json)
Constraint: dist/ is gitignored but tracked; force-add required for new files
Confidence: high
Scope-risk: narrow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-20 14:02:16 +00:00

7371 lines
266 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/cli/tmux-utils.ts
function tmuxEnv() {
const { TMUX: _, ...env } = process.env;
return env;
}
function resolveEnv(opts) {
return opts?.stripTmux ? tmuxEnv() : process.env;
}
function quoteForCmd(arg) {
if (arg.length === 0) return '""';
if (!/[\s"%^&|<>()]/.test(arg)) return arg;
return `"${arg.replace(/(["%])/g, "$1$1")}"`;
}
function resolveTmuxInvocation(args) {
const resolvedBinary = resolveTmuxBinaryPath();
if (process.platform === "win32" && /\.(cmd|bat)$/i.test(resolvedBinary)) {
const comspec = process.env.COMSPEC || "cmd.exe";
const commandLine = [quoteForCmd(resolvedBinary), ...args.map(quoteForCmd)].join(" ");
return {
command: comspec,
args: ["/d", "/s", "/c", commandLine]
};
}
return {
command: resolvedBinary,
args
};
}
function tmuxExec(args, opts) {
const { stripTmux: _, ...execOpts } = opts ?? {};
const invocation = resolveTmuxInvocation(args);
return (0, import_child_process.execFileSync)(invocation.command, invocation.args, { encoding: "utf-8", ...execOpts, env: resolveEnv(opts) });
}
async function tmuxExecAsync(args, opts) {
const { stripTmux: _, timeout, ...rest } = opts ?? {};
const invocation = resolveTmuxInvocation(args);
return (0, import_util.promisify)(import_child_process.execFile)(invocation.command, invocation.args, {
encoding: "utf-8",
env: resolveEnv(opts),
...timeout !== void 0 ? { timeout } : {},
...rest
});
}
function tmuxShell(command, opts) {
const { stripTmux: _, ...execOpts } = opts ?? {};
return (0, import_child_process.execSync)(`tmux ${command}`, { encoding: "utf-8", ...execOpts, env: resolveEnv(opts) });
}
async function tmuxShellAsync(command, opts) {
const { stripTmux: _, timeout, ...rest } = opts ?? {};
return (0, import_util.promisify)(import_child_process.exec)(`tmux ${command}`, {
encoding: "utf-8",
env: resolveEnv(opts),
...timeout !== void 0 ? { timeout } : {},
...rest
});
}
async function tmuxCmdAsync(args, opts) {
if (args.some((a) => a.includes("#{"))) {
const escaped = args.map((a) => "'" + a.replace(/'/g, "'\\''") + "'").join(" ");
return tmuxShellAsync(escaped, opts);
}
return tmuxExecAsync(args, opts);
}
function resolveTmuxBinaryPath() {
if (process.platform !== "win32") {
return "tmux";
}
try {
const result = (0, import_child_process.spawnSync)("where", ["tmux"], {
timeout: 5e3,
encoding: "utf8"
});
if (result.status !== 0) return "tmux";
const candidates = result.stdout?.split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
const first = candidates[0];
if (first && ((0, import_path.isAbsolute)(first) || import_path.win32.isAbsolute(first))) {
return first;
}
} catch {
}
return "tmux";
}
var import_child_process, import_path, import_util;
var init_tmux_utils = __esm({
"src/cli/tmux-utils.ts"() {
"use strict";
import_child_process = require("child_process");
import_path = require("path");
import_util = require("util");
}
});
// src/team/team-name.ts
function validateTeamName(teamName) {
if (!TEAM_NAME_PATTERN.test(teamName)) {
throw new Error(
`Invalid team name: "${teamName}". Team name must match /^[a-z0-9][a-z0-9-]{0,48}[a-z0-9]$/.`
);
}
return teamName;
}
var TEAM_NAME_PATTERN;
var init_team_name = __esm({
"src/team/team-name.ts"() {
"use strict";
TEAM_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,48}[a-z0-9]$/;
}
});
// src/team/tmux-session.ts
var tmux_session_exports = {};
__export(tmux_session_exports, {
applyMainVerticalLayout: () => applyMainVerticalLayout,
buildWorkerLaunchSpec: () => buildWorkerLaunchSpec,
buildWorkerStartCommand: () => buildWorkerStartCommand,
createSession: () => createSession,
createTeamSession: () => createTeamSession,
detectTeamMultiplexerContext: () => detectTeamMultiplexerContext,
getDefaultShell: () => getDefaultShell,
injectToLeaderPane: () => injectToLeaderPane,
isSessionAlive: () => isSessionAlive,
isUnixLikeOnWindows: () => isUnixLikeOnWindows,
isWorkerAlive: () => isWorkerAlive,
killSession: () => killSession,
killTeamSession: () => killTeamSession,
killWorkerPanes: () => killWorkerPanes,
listActiveSessions: () => listActiveSessions,
paneHasActiveTask: () => paneHasActiveTask,
paneLooksReady: () => paneLooksReady,
resolveShellFromCandidates: () => resolveShellFromCandidates,
resolveSplitPaneWorkerPaneIds: () => resolveSplitPaneWorkerPaneIds,
resolveSupportedShellAffinity: () => resolveSupportedShellAffinity,
sanitizeName: () => sanitizeName,
sendToWorker: () => sendToWorker,
sessionName: () => sessionName,
shouldAttemptAdaptiveRetry: () => shouldAttemptAdaptiveRetry,
spawnBridgeInSession: () => spawnBridgeInSession,
spawnWorkerInPane: () => spawnWorkerInPane,
validateTmux: () => validateTmux,
waitForPaneReady: () => waitForPaneReady
});
function detectTeamMultiplexerContext(env = process.env) {
if (env.TMUX) return "tmux";
if (env.CMUX_SURFACE_ID) return "cmux";
return "none";
}
function isUnixLikeOnWindows() {
return process.platform === "win32" && !!(process.env.MSYSTEM || process.env.MINGW_PREFIX);
}
async function applyMainVerticalLayout(teamTarget) {
try {
await tmuxExecAsync(["select-layout", "-t", teamTarget, "main-vertical"]);
} catch {
}
try {
const widthResult = await tmuxCmdAsync([
"display-message",
"-p",
"-t",
teamTarget,
"#{window_width}"
]);
const width = parseInt(widthResult.stdout.trim(), 10);
if (Number.isFinite(width) && width >= 40) {
const half = String(Math.floor(width / 2));
await tmuxExecAsync(["set-window-option", "-t", teamTarget, "main-pane-width", half]);
await tmuxExecAsync(["select-layout", "-t", teamTarget, "main-vertical"]);
}
} catch {
}
}
function getDefaultShell() {
if (process.platform === "win32" && !isUnixLikeOnWindows()) {
return process.env.COMSPEC || "cmd.exe";
}
const shell = process.env.SHELL || "/bin/bash";
const name = (0, import_path8.basename)(shell.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
if (!SUPPORTED_POSIX_SHELLS.has(name)) {
return "/bin/sh";
}
return shell;
}
function pathEntries(envPath) {
return (envPath ?? "").split(process.platform === "win32" ? ";" : ":").map((entry) => entry.trim()).filter(Boolean);
}
function pathCandidateNames(candidatePath) {
const base = (0, import_path8.basename)(candidatePath.replace(/\\/g, "/"));
const bare = base.replace(/\.(exe|cmd|bat)$/i, "");
if (process.platform === "win32") {
return Array.from(/* @__PURE__ */ new Set([`${bare}.exe`, `${bare}.cmd`, `${bare}.bat`, bare]));
}
return Array.from(/* @__PURE__ */ new Set([base, bare]));
}
function resolveShellFromPath(candidatePath) {
for (const dir of pathEntries(process.env.PATH)) {
for (const name of pathCandidateNames(candidatePath)) {
const full = (0, import_path8.join)(dir, name);
if ((0, import_fs5.existsSync)(full)) return full;
}
}
return null;
}
function resolveShellFromCandidates(paths, rcFile) {
for (const p of paths) {
if ((0, import_fs5.existsSync)(p)) return { shell: p, rcFile };
const resolvedFromPath = resolveShellFromPath(p);
if (resolvedFromPath) return { shell: resolvedFromPath, rcFile };
}
return null;
}
function resolveSupportedShellAffinity(shellPath) {
if (!shellPath) return null;
const name = (0, import_path8.basename)(shellPath.replace(/\\/g, "/")).replace(/\.(exe|cmd|bat)$/i, "");
if (name !== "zsh" && name !== "bash") return null;
if (!(0, import_fs5.existsSync)(shellPath)) return null;
const home = process.env.HOME ?? "";
const rcFile = home ? `${home}/.${name}rc` : null;
return { shell: shellPath, rcFile };
}
function buildWorkerLaunchSpec(shellPath) {
if (isUnixLikeOnWindows()) {
return { shell: "/bin/sh", rcFile: null };
}
const preferred = resolveSupportedShellAffinity(shellPath);
if (preferred) return preferred;
const home = process.env.HOME ?? "";
const zshRc = home ? `${home}/.zshrc` : null;
const zsh = resolveShellFromCandidates(ZSH_CANDIDATES, zshRc ?? "");
if (zsh) return { shell: zsh.shell, rcFile: zshRc };
const bashRc = home ? `${home}/.bashrc` : null;
const bash = resolveShellFromCandidates(BASH_CANDIDATES, bashRc ?? "");
if (bash) return { shell: bash.shell, rcFile: bashRc };
return { shell: "/bin/sh", rcFile: null };
}
function escapeForCmdSet(value) {
return value.replace(/"/g, '""');
}
function shellNameFromPath(shellPath) {
const shellName = (0, import_path8.basename)(shellPath.replace(/\\/g, "/"));
return shellName.replace(/\.(exe|cmd|bat)$/i, "");
}
function shellEscape(value) {
return `'${value.replace(/'/g, `'"'"'`)}'`;
}
function assertSafeEnvKey(key) {
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
throw new Error(`Invalid environment key: "${key}"`);
}
}
function isAbsoluteLaunchBinaryPath(value) {
return (0, import_path8.isAbsolute)(value) || import_path8.win32.isAbsolute(value);
}
function assertSafeLaunchBinary(launchBinary) {
if (launchBinary.trim().length === 0) {
throw new Error("Invalid launchBinary: value cannot be empty");
}
if (launchBinary !== launchBinary.trim()) {
throw new Error("Invalid launchBinary: value cannot have leading/trailing whitespace");
}
if (DANGEROUS_LAUNCH_BINARY_CHARS.test(launchBinary)) {
throw new Error("Invalid launchBinary: contains dangerous shell metacharacters");
}
if (/\s/.test(launchBinary) && !isAbsoluteLaunchBinaryPath(launchBinary)) {
throw new Error("Invalid launchBinary: paths with spaces must be absolute");
}
}
function getLaunchWords(config) {
if (config.launchBinary) {
assertSafeLaunchBinary(config.launchBinary);
return [config.launchBinary, ...config.launchArgs ?? []];
}
if (config.launchCmd) {
throw new Error(
"launchCmd is deprecated and has been removed for security reasons. Use launchBinary + launchArgs instead."
);
}
throw new Error("Missing worker launch command. Provide launchBinary or launchCmd.");
}
function buildWorkerStartCommand(config) {
const shell = getDefaultShell();
const launchSpec = buildWorkerLaunchSpec(process.env.SHELL);
const launchWords = getLaunchWords(config);
const shouldSourceRc = process.env.OMC_TEAM_NO_RC !== "1";
if (process.platform === "win32" && !isUnixLikeOnWindows()) {
const envPrefix = Object.entries(config.envVars).map(([k, v]) => {
assertSafeEnvKey(k);
return `set "${k}=${escapeForCmdSet(v)}"`;
}).join(" && ");
const launch = config.launchBinary ? launchWords.map((part) => `"${escapeForCmdSet(part)}"`).join(" ") : launchWords[0];
const cmdBody = envPrefix ? `${envPrefix} && ${launch}` : launch;
return `${shell} /d /s /c "${cmdBody}"`;
}
if (config.launchBinary) {
const envAssignments = Object.entries(config.envVars).map(([key, value]) => {
assertSafeEnvKey(key);
return `${key}=${shellEscape(value)}`;
});
const shellName2 = shellNameFromPath(shell) || "bash";
const isFish2 = shellName2 === "fish";
const execArgsCommand = isFish2 ? "exec $argv" : 'exec "$@"';
let rcFile2 = (launchSpec.shell === shell ? launchSpec.rcFile : null) ?? "";
if (!rcFile2 && process.env.HOME) {
rcFile2 = isFish2 ? `${process.env.HOME}/.config/fish/config.fish` : `${process.env.HOME}/.${shellName2}rc`;
}
let script;
if (isFish2) {
script = shouldSourceRc && rcFile2 ? `test -f ${shellEscape(rcFile2)}; and source ${shellEscape(rcFile2)}; ${execArgsCommand}` : execArgsCommand;
} else {
script = shouldSourceRc && rcFile2 ? `[ -f ${shellEscape(rcFile2)} ] && . ${shellEscape(rcFile2)}; ${execArgsCommand}` : execArgsCommand;
}
const shellFlags = isFish2 ? ["-l", "-c"] : ["-lc"];
return [
shellEscape("env"),
...envAssignments,
...[shell, ...shellFlags, script, "--", ...launchWords].map(shellEscape)
].join(" ");
}
const envString = Object.entries(config.envVars).map(([k, v]) => {
assertSafeEnvKey(k);
return `${k}=${shellEscape(v)}`;
}).join(" ");
const shellName = shellNameFromPath(shell) || "bash";
const isFish = shellName === "fish";
let rcFile = (launchSpec.shell === shell ? launchSpec.rcFile : null) ?? "";
if (!rcFile && process.env.HOME) {
rcFile = isFish ? `${process.env.HOME}/.config/fish/config.fish` : `${process.env.HOME}/.${shellName}rc`;
}
let sourceCmd = "";
if (shouldSourceRc && rcFile) {
sourceCmd = isFish ? `test -f "${rcFile}"; and source "${rcFile}"; ` : `[ -f "${rcFile}" ] && source "${rcFile}"; `;
}
return `env ${envString} ${shell} -c "${sourceCmd}exec ${launchWords[0]}"`;
}
function validateTmux(hasTmuxContext = false) {
if (hasTmuxContext) {
return;
}
try {
tmuxShell("-V", { stripTmux: true, timeout: 5e3, stdio: "pipe" });
} catch {
throw new Error(
"tmux is not available. Install it:\n macOS: brew install tmux\n Ubuntu/Debian: sudo apt-get install tmux\n Fedora: sudo dnf install tmux\n Arch: sudo pacman -S tmux\n Windows: winget install psmux"
);
}
}
function sanitizeName(name) {
const sanitized = name.replace(/[^a-zA-Z0-9-]/g, "");
if (sanitized.length === 0) {
throw new Error(`Invalid name: "${name}" contains no valid characters (alphanumeric or hyphen)`);
}
if (sanitized.length < 2) {
throw new Error(`Invalid name: "${name}" too short after sanitization (minimum 2 characters)`);
}
return sanitized.slice(0, 50);
}
function sessionName(teamName, workerName2) {
return `${TMUX_SESSION_PREFIX}-${sanitizeName(teamName)}-${sanitizeName(workerName2)}`;
}
function createSession(teamName, workerName2, workingDirectory) {
const name = sessionName(teamName, workerName2);
try {
tmuxExec(["kill-session", "-t", name], { stripTmux: true, stdio: "pipe", timeout: 5e3 });
} catch {
}
const args = ["new-session", "-d", "-s", name, "-x", "200", "-y", "50"];
if (workingDirectory) {
args.push("-c", workingDirectory);
}
tmuxExec(args, { stripTmux: true, stdio: "pipe", timeout: 5e3 });
return name;
}
function killSession(teamName, workerName2) {
const name = sessionName(teamName, workerName2);
try {
tmuxExec(["kill-session", "-t", name], { stripTmux: true, stdio: "pipe", timeout: 5e3 });
} catch {
}
}
function isSessionAlive(teamName, workerName2) {
const name = sessionName(teamName, workerName2);
try {
tmuxExec(["has-session", "-t", name], { stripTmux: true, stdio: "pipe", timeout: 5e3 });
return true;
} catch {
return false;
}
}
function listActiveSessions(teamName) {
const prefix = `${TMUX_SESSION_PREFIX}-${sanitizeName(teamName)}-`;
try {
const output = tmuxShell("list-sessions -F '#{session_name}'", {
timeout: 5e3,
stdio: ["pipe", "pipe", "pipe"]
});
return output.trim().split("\n").filter((s) => s.startsWith(prefix)).map((s) => s.slice(prefix.length));
} catch {
return [];
}
}
function spawnBridgeInSession(tmuxSession, bridgeScriptPath, configFilePath) {
const cmd = `node "${bridgeScriptPath}" --config "${configFilePath}"`;
tmuxExec(["send-keys", "-t", tmuxSession, cmd, "Enter"], { stripTmux: true, stdio: "pipe", timeout: 5e3 });
}
async function createTeamSession(teamName, workerCount, cwd, options = {}) {
const multiplexerContext = detectTeamMultiplexerContext();
const inTmux = multiplexerContext === "tmux";
const useDedicatedWindow = Boolean(options.newWindow && inTmux);
if (!inTmux) {
validateTmux();
}
const envPaneIdRaw = (process.env.TMUX_PANE ?? "").trim();
const envPaneId = /^%\d+$/.test(envPaneIdRaw) ? envPaneIdRaw : "";
let sessionAndWindow = "";
let leaderPaneId = envPaneId;
let sessionMode = inTmux ? "split-pane" : "detached-session";
if (!inTmux) {
const detachedSessionName = `${TMUX_SESSION_PREFIX}-${sanitizeName(teamName)}-${Date.now().toString(36)}`;
const detachedResult = await tmuxExecAsync([
"new-session",
"-d",
"-P",
"-F",
"#S:0 #{pane_id}",
"-s",
detachedSessionName,
"-c",
cwd
], { stripTmux: true });
const detachedLine = detachedResult.stdout.trim();
const detachedMatch = detachedLine.match(/^(\S+)\s+(%\d+)$/);
if (!detachedMatch) {
throw new Error(`Failed to create detached tmux session: "${detachedLine}"`);
}
sessionAndWindow = detachedMatch[1];
leaderPaneId = detachedMatch[2];
}
if (inTmux && envPaneId) {
try {
const targetedContextResult = await tmuxExecAsync([
"display-message",
"-p",
"-t",
envPaneId,
"#S:#I"
]);
sessionAndWindow = targetedContextResult.stdout.trim();
} catch {
sessionAndWindow = "";
leaderPaneId = "";
}
}
if (!sessionAndWindow || !leaderPaneId) {
const contextResult = await tmuxCmdAsync([
"display-message",
"-p",
"#S:#I #{pane_id}"
]);
const contextLine = contextResult.stdout.trim();
const contextMatch = contextLine.match(/^(\S+)\s+(%\d+)$/);
if (!contextMatch) {
throw new Error(`Failed to resolve tmux context: "${contextLine}"`);
}
sessionAndWindow = contextMatch[1];
leaderPaneId = contextMatch[2];
}
if (useDedicatedWindow) {
const targetSession = sessionAndWindow.split(":")[0] ?? sessionAndWindow;
const windowName = `omc-${sanitizeName(teamName)}`.slice(0, 32);
const newWindowResult = await tmuxExecAsync([
"new-window",
"-d",
"-P",
"-F",
"#S:#I #{pane_id}",
"-t",
targetSession,
"-n",
windowName,
"-c",
cwd
]);
const newWindowLine = newWindowResult.stdout.trim();
const newWindowMatch = newWindowLine.match(/^(\S+)\s+(%\d+)$/);
if (!newWindowMatch) {
throw new Error(`Failed to create team tmux window: "${newWindowLine}"`);
}
sessionAndWindow = newWindowMatch[1];
leaderPaneId = newWindowMatch[2];
sessionMode = "dedicated-window";
}
const teamTarget = sessionAndWindow;
const resolvedSessionName = teamTarget.split(":")[0];
const workerPaneIds = [];
if (workerCount <= 0) {
try {
await tmuxExecAsync(["set-option", "-t", resolvedSessionName, "mouse", "on"]);
} catch {
}
if (sessionMode !== "dedicated-window") {
try {
await tmuxExecAsync(["select-pane", "-t", leaderPaneId]);
} catch {
}
}
await new Promise((r) => setTimeout(r, 300));
return { sessionName: teamTarget, leaderPaneId, workerPaneIds, sessionMode };
}
for (let i = 0; i < workerCount; i++) {
const splitTarget = i === 0 ? leaderPaneId : workerPaneIds[i - 1];
const splitType = i === 0 ? "-h" : "-v";
const splitResult = await tmuxCmdAsync([
"split-window",
splitType,
"-t",
splitTarget,
"-d",
"-P",
"-F",
"#{pane_id}",
"-c",
cwd
]);
const paneId = splitResult.stdout.split("\n")[0]?.trim();
if (paneId) {
workerPaneIds.push(paneId);
}
}
await applyMainVerticalLayout(teamTarget);
try {
await tmuxExecAsync(["set-option", "-t", resolvedSessionName, "mouse", "on"]);
} catch {
}
if (sessionMode !== "dedicated-window") {
try {
await tmuxExecAsync(["select-pane", "-t", leaderPaneId]);
} catch {
}
}
await new Promise((r) => setTimeout(r, 300));
return { sessionName: teamTarget, leaderPaneId, workerPaneIds, sessionMode };
}
async function spawnWorkerInPane(sessionName2, paneId, config) {
validateTeamName(config.teamName);
const startCmd = buildWorkerStartCommand(config);
await tmuxExecAsync([
"send-keys",
"-t",
paneId,
"-l",
startCmd
]);
await tmuxExecAsync(["send-keys", "-t", paneId, "Enter"]);
}
function normalizeTmuxCapture(value) {
return value.replace(/\r/g, "").replace(/\s+/g, " ").trim();
}
async function capturePaneAsync(paneId) {
try {
const result = await tmuxExecAsync(["capture-pane", "-t", paneId, "-p", "-S", "-80"]);
return result.stdout;
} catch {
return "";
}
}
function paneHasTrustPrompt(captured) {
const lines = captured.split("\n").map((l) => l.replace(/\r/g, "").trim()).filter((l) => l.length > 0);
const tail = lines.slice(-12);
const hasQuestion = tail.some((l) => /Do you trust the contents of this directory\?/i.test(l));
const hasChoices = tail.some((l) => /Yes,\s*continue|No,\s*quit|Press enter to continue/i.test(l));
return hasQuestion && hasChoices;
}
function paneIsBootstrapping(captured) {
const lines = captured.split("\n").map((line) => line.replace(/\r/g, "").trim()).filter((line) => line.length > 0);
return lines.some(
(line) => /\b(loading|initializing|starting up)\b/i.test(line) || /\bmodel:\s*loading\b/i.test(line) || /\bconnecting\s+to\b/i.test(line)
);
}
function paneHasActiveTask(captured) {
const lines = captured.split("\n").map((l) => l.replace(/\r/g, "").trim()).filter((l) => l.length > 0);
const tail = lines.slice(-40);
if (tail.some((l) => /\b\d+\s+background terminal running\b/i.test(l))) return true;
if (tail.some((l) => /esc to interrupt/i.test(l))) return true;
if (tail.some((l) => /\bbackground terminal running\b/i.test(l))) return true;
if (tail.some((l) => /^[·]\s+[A-Za-z][A-Za-z0-9''-]*(?:\s+[A-Za-z][A-Za-z0-9''-]*){0,3}(?:|\.{3})$/u.test(l))) return true;
return false;
}
function paneLooksReady(captured) {
const content = captured.trimEnd();
if (content === "") return false;
const lines = content.split("\n").map((line) => line.replace(/\r/g, "").trimEnd()).filter((line) => line.trim() !== "");
if (lines.length === 0) return false;
if (paneIsBootstrapping(content)) return false;
const lastLine = lines[lines.length - 1];
if (/^\s*[>]\s*/u.test(lastLine)) return true;
const hasCodexPromptLine = lines.some((line) => /^\s*\s*/u.test(line));
const hasClaudePromptLine = lines.some((line) => /^\s*\s*/u.test(line));
return hasCodexPromptLine || hasClaudePromptLine;
}
async function waitForPaneReady(paneId, opts = {}) {
const envTimeout = Number.parseInt(process.env.OMC_SHELL_READY_TIMEOUT_MS ?? "", 10);
const timeoutMs = Number.isFinite(opts.timeoutMs) && (opts.timeoutMs ?? 0) > 0 ? Number(opts.timeoutMs) : Number.isFinite(envTimeout) && envTimeout > 0 ? envTimeout : 3e4;
const pollIntervalMs = Number.isFinite(opts.pollIntervalMs) && (opts.pollIntervalMs ?? 0) > 0 ? Number(opts.pollIntervalMs) : 250;
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const captured = await capturePaneAsync(paneId);
if (paneLooksReady(captured) && !paneHasActiveTask(captured)) {
return true;
}
await sleep(pollIntervalMs);
}
console.warn(
`[tmux-session] waitForPaneReady: pane ${paneId} timed out after ${timeoutMs}ms (set OMC_SHELL_READY_TIMEOUT_MS to tune)`
);
return false;
}
function paneTailContainsLiteralLine(captured, text) {
return normalizeTmuxCapture(captured).includes(normalizeTmuxCapture(text));
}
async function paneInCopyMode(paneId) {
try {
const result = await tmuxCmdAsync(["display-message", "-t", paneId, "-p", "#{pane_in_mode}"]);
return result.stdout.trim() === "1";
} catch {
return false;
}
}
function shouldAttemptAdaptiveRetry(args) {
if (process.env.OMC_TEAM_AUTO_INTERRUPT_RETRY === "0") return false;
if (args.retriesAttempted >= 1) return false;
if (args.paneInCopyMode) return false;
if (!args.paneBusy) return false;
if (typeof args.latestCapture !== "string") return false;
if (!paneTailContainsLiteralLine(args.latestCapture, args.message)) return false;
if (paneHasActiveTask(args.latestCapture)) return false;
if (!paneLooksReady(args.latestCapture)) return false;
return true;
}
async function sendToWorker(_sessionName, paneId, message) {
if (message.length > 200) {
console.warn(`[tmux-session] sendToWorker: message rejected (${message.length} chars exceeds 200 char limit)`);
return false;
}
try {
const sendKey = async (key) => {
await tmuxExecAsync(["send-keys", "-t", paneId, key]);
};
if (await paneInCopyMode(paneId)) {
return false;
}
const initialCapture = await capturePaneAsync(paneId);
const paneBusy = paneHasActiveTask(initialCapture);
if (paneHasTrustPrompt(initialCapture)) {
await sendKey("C-m");
await sleep(120);
await sendKey("C-m");
await sleep(200);
}
await tmuxExecAsync(["send-keys", "-t", paneId, "-l", "--", message]);
await sleep(150);
const submitRounds = 6;
for (let round = 0; round < submitRounds; round++) {
await sleep(100);
if (round === 0 && paneBusy) {
await sendKey("Tab");
await sleep(80);
await sendKey("C-m");
} else {
await sendKey("C-m");
await sleep(200);
await sendKey("C-m");
}
await sleep(140);
const checkCapture = await capturePaneAsync(paneId);
if (!paneTailContainsLiteralLine(checkCapture, message)) return true;
await sleep(140);
}
if (await paneInCopyMode(paneId)) {
return false;
}
const finalCapture = await capturePaneAsync(paneId);
const paneModeBeforeAdaptiveRetry = await paneInCopyMode(paneId);
if (shouldAttemptAdaptiveRetry({
paneBusy,
latestCapture: finalCapture,
message,
paneInCopyMode: paneModeBeforeAdaptiveRetry,
retriesAttempted: 0
})) {
if (await paneInCopyMode(paneId)) {
return false;
}
await sendKey("C-u");
await sleep(80);
if (await paneInCopyMode(paneId)) {
return false;
}
await tmuxExecAsync(["send-keys", "-t", paneId, "-l", "--", message]);
await sleep(120);
for (let round = 0; round < 4; round++) {
await sendKey("C-m");
await sleep(180);
await sendKey("C-m");
await sleep(140);
const retryCapture = await capturePaneAsync(paneId);
if (!paneTailContainsLiteralLine(retryCapture, message)) return true;
}
}
if (await paneInCopyMode(paneId)) {
return false;
}
await sendKey("C-m");
await sleep(120);
await sendKey("C-m");
await sleep(140);
const finalCheckCapture = await capturePaneAsync(paneId);
if (!finalCheckCapture || finalCheckCapture.trim() === "") {
return false;
}
return !paneTailContainsLiteralLine(finalCheckCapture, message);
} catch {
return false;
}
}
async function injectToLeaderPane(sessionName2, leaderPaneId, message) {
const prefixed = `[OMC_TMUX_INJECT] ${message}`.slice(0, 200);
try {
if (await paneInCopyMode(leaderPaneId)) {
return false;
}
const captured = await capturePaneAsync(leaderPaneId);
if (paneHasActiveTask(captured)) {
await tmuxExecAsync(["send-keys", "-t", leaderPaneId, "C-c"]);
await new Promise((r) => setTimeout(r, 250));
}
} catch {
}
return sendToWorker(sessionName2, leaderPaneId, prefixed);
}
async function isWorkerAlive(paneId) {
try {
const result = await tmuxCmdAsync([
"display-message",
"-t",
paneId,
"-p",
"#{pane_dead}"
]);
return result.stdout.trim() === "0";
} catch {
return false;
}
}
async function killWorkerPanes(opts) {
const { paneIds, leaderPaneId, teamName, cwd, graceMs = 1e4 } = opts;
if (!paneIds.length) return;
const shutdownPath = (0, import_path8.join)(cwd, ".omc", "state", "team", teamName, "shutdown.json");
try {
await import_promises.default.writeFile(shutdownPath, JSON.stringify({ requestedAt: Date.now() }));
const aliveChecks = await Promise.all(paneIds.map((id) => isWorkerAlive(id)));
if (aliveChecks.some((alive) => alive)) {
await sleep(graceMs);
}
} catch {
}
for (const paneId of paneIds) {
if (paneId === leaderPaneId) continue;
try {
await tmuxExecAsync(["kill-pane", "-t", paneId]);
} catch {
}
}
}
function isPaneId(value) {
return typeof value === "string" && /^%\d+$/.test(value.trim());
}
function dedupeWorkerPaneIds(paneIds, leaderPaneId) {
const unique = /* @__PURE__ */ new Set();
for (const paneId of paneIds) {
if (!isPaneId(paneId)) continue;
const normalized = paneId.trim();
if (normalized === leaderPaneId) continue;
unique.add(normalized);
}
return [...unique];
}
async function resolveSplitPaneWorkerPaneIds(sessionName2, recordedPaneIds, leaderPaneId) {
const resolved = dedupeWorkerPaneIds(recordedPaneIds ?? [], leaderPaneId);
if (!sessionName2.includes(":")) return resolved;
try {
const paneResult = await tmuxCmdAsync(["list-panes", "-t", sessionName2, "-F", "#{pane_id}"]);
return dedupeWorkerPaneIds(
[...resolved, ...paneResult.stdout.split("\n").map((paneId) => paneId.trim())],
leaderPaneId
);
} catch {
return resolved;
}
}
async function killTeamSession(sessionName2, workerPaneIds, leaderPaneId, options = {}) {
const sessionMode = options.sessionMode ?? (sessionName2.includes(":") ? "split-pane" : "detached-session");
if (sessionMode === "split-pane") {
if (!workerPaneIds?.length) return;
for (const id of workerPaneIds) {
if (id === leaderPaneId) continue;
try {
await tmuxExecAsync(["kill-pane", "-t", id]);
} catch {
}
}
return;
}
if (sessionMode === "dedicated-window") {
try {
await tmuxExecAsync(["kill-window", "-t", sessionName2]);
} catch {
}
return;
}
const sessionTarget = sessionName2.split(":")[0] ?? sessionName2;
if (process.env.OMC_TEAM_ALLOW_KILL_CURRENT_SESSION !== "1" && process.env.TMUX) {
try {
const current = await tmuxCmdAsync(["display-message", "-p", "#S"]);
const currentSessionName = current.stdout.trim();
if (currentSessionName && currentSessionName === sessionTarget) {
return;
}
} catch {
}
}
try {
await tmuxExecAsync(["kill-session", "-t", sessionTarget]);
} catch {
}
}
var import_fs5, import_path8, import_promises, sleep, TMUX_SESSION_PREFIX, SUPPORTED_POSIX_SHELLS, ZSH_CANDIDATES, BASH_CANDIDATES, DANGEROUS_LAUNCH_BINARY_CHARS;
var init_tmux_session = __esm({
"src/team/tmux-session.ts"() {
"use strict";
import_fs5 = require("fs");
import_path8 = require("path");
import_promises = __toESM(require("fs/promises"), 1);
init_team_name();
init_tmux_utils();
sleep = (ms) => new Promise((r) => setTimeout(r, ms));
TMUX_SESSION_PREFIX = "omc-team";
SUPPORTED_POSIX_SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "fish", "ksh"]);
ZSH_CANDIDATES = ["/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh", "/opt/homebrew/bin/zsh"];
BASH_CANDIDATES = ["/bin/bash", "/usr/bin/bash"];
DANGEROUS_LAUNCH_BINARY_CHARS = /[;&|`$()<>\n\r\t\0]/;
}
});
// src/lib/atomic-write.ts
function ensureDirSync(dir) {
if (fsSync.existsSync(dir)) {
return;
}
try {
fsSync.mkdirSync(dir, { recursive: true });
} catch (err) {
if (err.code === "EEXIST") {
return;
}
throw err;
}
}
var fs2, fsSync, path, crypto;
var init_atomic_write = __esm({
"src/lib/atomic-write.ts"() {
"use strict";
fs2 = __toESM(require("fs/promises"), 1);
fsSync = __toESM(require("fs"), 1);
path = __toESM(require("path"), 1);
crypto = __toESM(require("crypto"), 1);
}
});
// src/platform/process-utils.ts
function isProcessAlive(pid) {
if (!Number.isInteger(pid) || pid <= 0) return false;
try {
process.kill(pid, 0);
return true;
} catch (e) {
if (e && typeof e === "object" && "code" in e && e.code === "EPERM") {
return true;
}
return false;
}
}
var import_child_process4, import_util2, fsPromises, execFileAsync;
var init_process_utils = __esm({
"src/platform/process-utils.ts"() {
"use strict";
import_child_process4 = require("child_process");
import_util2 = require("util");
fsPromises = __toESM(require("fs/promises"), 1);
execFileAsync = (0, import_util2.promisify)(import_child_process4.execFile);
}
});
// src/platform/index.ts
var path2, import_fs8, PLATFORM;
var init_platform = __esm({
"src/platform/index.ts"() {
"use strict";
path2 = __toESM(require("path"), 1);
import_fs8 = require("fs");
init_process_utils();
PLATFORM = process.platform;
}
});
// src/lib/file-lock.ts
var file_lock_exports = {};
__export(file_lock_exports, {
acquireFileLock: () => acquireFileLock,
acquireFileLockSync: () => acquireFileLockSync,
lockPathFor: () => lockPathFor,
releaseFileLock: () => releaseFileLock,
releaseFileLockSync: () => releaseFileLockSync,
withFileLock: () => withFileLock,
withFileLockSync: () => withFileLockSync
});
function isLockStale(lockPath, staleLockMs) {
try {
const stat2 = (0, import_fs9.statSync)(lockPath);
const ageMs = Date.now() - stat2.mtimeMs;
if (ageMs < staleLockMs) return false;
try {
const raw = (0, import_fs9.readFileSync)(lockPath, "utf-8");
const payload = JSON.parse(raw);
if (payload.pid && isProcessAlive(payload.pid)) return false;
} catch {
}
return true;
} catch {
return false;
}
}
function lockPathFor(filePath) {
return filePath + ".lock";
}
function tryAcquireSync(lockPath, staleLockMs) {
ensureDirSync(path3.dirname(lockPath));
try {
const fd = (0, import_fs9.openSync)(
lockPath,
import_fs9.constants.O_CREAT | import_fs9.constants.O_EXCL | import_fs9.constants.O_WRONLY,
384
);
try {
const payload = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
(0, import_fs9.writeSync)(fd, payload, null, "utf-8");
} catch (writeErr) {
try {
(0, import_fs9.closeSync)(fd);
} catch {
}
try {
(0, import_fs9.unlinkSync)(lockPath);
} catch {
}
throw writeErr;
}
return { fd, path: lockPath };
} catch (err) {
if (err && typeof err === "object" && "code" in err && err.code === "EEXIST") {
if (isLockStale(lockPath, staleLockMs)) {
try {
(0, import_fs9.unlinkSync)(lockPath);
} catch {
}
try {
const fd = (0, import_fs9.openSync)(
lockPath,
import_fs9.constants.O_CREAT | import_fs9.constants.O_EXCL | import_fs9.constants.O_WRONLY,
384
);
try {
const payload = JSON.stringify({ pid: process.pid, timestamp: Date.now() });
(0, import_fs9.writeSync)(fd, payload, null, "utf-8");
} catch (writeErr) {
try {
(0, import_fs9.closeSync)(fd);
} catch {
}
try {
(0, import_fs9.unlinkSync)(lockPath);
} catch {
}
throw writeErr;
}
return { fd, path: lockPath };
} catch {
return null;
}
}
return null;
}
throw err;
}
}
function acquireFileLockSync(lockPath, opts) {
const staleLockMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
const timeoutMs = opts?.timeoutMs ?? 0;
const retryDelayMs = opts?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
const handle = tryAcquireSync(lockPath, staleLockMs);
if (handle || timeoutMs <= 0) return handle;
const deadline = Date.now() + timeoutMs;
const sharedBuf = new SharedArrayBuffer(4);
const sharedArr = new Int32Array(sharedBuf);
while (Date.now() < deadline) {
const waitMs = Math.min(retryDelayMs, deadline - Date.now());
try {
Atomics.wait(sharedArr, 0, 0, waitMs);
} catch {
const waitUntil = Date.now() + waitMs;
while (Date.now() < waitUntil) {
}
}
const retryHandle = tryAcquireSync(lockPath, staleLockMs);
if (retryHandle) return retryHandle;
}
return null;
}
function releaseFileLockSync(handle) {
try {
(0, import_fs9.closeSync)(handle.fd);
} catch {
}
try {
(0, import_fs9.unlinkSync)(handle.path);
} catch {
}
}
function withFileLockSync(lockPath, fn, opts) {
const handle = acquireFileLockSync(lockPath, opts);
if (!handle) {
throw new Error(`Failed to acquire file lock: ${lockPath}`);
}
try {
return fn();
} finally {
releaseFileLockSync(handle);
}
}
function sleep2(ms) {
return new Promise((resolve5) => setTimeout(resolve5, ms));
}
async function acquireFileLock(lockPath, opts) {
const staleLockMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS;
const timeoutMs = opts?.timeoutMs ?? 0;
const retryDelayMs = opts?.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
const handle = tryAcquireSync(lockPath, staleLockMs);
if (handle || timeoutMs <= 0) return handle;
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
await sleep2(Math.min(retryDelayMs, deadline - Date.now()));
const retryHandle = tryAcquireSync(lockPath, staleLockMs);
if (retryHandle) return retryHandle;
}
return null;
}
function releaseFileLock(handle) {
releaseFileLockSync(handle);
}
async function withFileLock(lockPath, fn, opts) {
const handle = await acquireFileLock(lockPath, opts);
if (!handle) {
throw new Error(`Failed to acquire file lock: ${lockPath}`);
}
try {
return await fn();
} finally {
releaseFileLock(handle);
}
}
var import_fs9, path3, DEFAULT_STALE_LOCK_MS, DEFAULT_RETRY_DELAY_MS;
var init_file_lock = __esm({
"src/lib/file-lock.ts"() {
"use strict";
import_fs9 = require("fs");
path3 = __toESM(require("path"), 1);
init_atomic_write();
init_platform();
DEFAULT_STALE_LOCK_MS = 3e4;
DEFAULT_RETRY_DELAY_MS = 50;
}
});
// src/team/runtime-cli.ts
var runtime_cli_exports = {};
__export(runtime_cli_exports, {
buildCliOutput: () => buildCliOutput,
buildTerminalCliResult: () => buildTerminalCliResult,
checkWatchdogFailedMarker: () => checkWatchdogFailedMarker,
getTerminalStatus: () => getTerminalStatus,
writeResultArtifact: () => writeResultArtifact
});
module.exports = __toCommonJS(runtime_cli_exports);
var import_fs18 = require("fs");
var import_promises8 = require("fs/promises");
var import_path20 = require("path");
// src/team/runtime.ts
var import_promises3 = require("fs/promises");
var import_path14 = require("path");
var import_fs11 = require("fs");
init_tmux_utils();
// src/team/model-contract.ts
var import_child_process2 = require("child_process");
var import_path7 = require("path");
init_team_name();
// src/agents/utils.ts
var import_fs = require("fs");
var import_path2 = require("path");
var import_url = require("url");
var import_meta = {};
function getPackageDir() {
if (typeof __dirname !== "undefined" && __dirname) {
const currentDirName = (0, import_path2.basename)(__dirname);
const parentDirName = (0, import_path2.basename)((0, import_path2.dirname)(__dirname));
if (currentDirName === "bridge") {
return (0, import_path2.join)(__dirname, "..");
}
if (currentDirName === "agents" && (parentDirName === "src" || parentDirName === "dist")) {
return (0, import_path2.join)(__dirname, "..", "..");
}
}
try {
const __filename = (0, import_url.fileURLToPath)(import_meta.url);
const __dirname2 = (0, import_path2.dirname)(__filename);
const currentDirName = (0, import_path2.basename)(__dirname2);
if (currentDirName === "bridge") {
return (0, import_path2.join)(__dirname2, "..");
}
return (0, import_path2.join)(__dirname2, "..", "..");
} catch {
}
return process.cwd();
}
function stripFrontmatter(content) {
const match = content.match(/^---[\s\S]*?---\s*([\s\S]*)$/);
return match ? match[1].trim() : content.trim();
}
function loadAgentPrompt(agentName) {
if (!/^[a-z0-9-]+$/i.test(agentName)) {
throw new Error(`Invalid agent name: contains disallowed characters`);
}
try {
if (typeof __AGENT_PROMPTS__ !== "undefined" && __AGENT_PROMPTS__ !== null) {
const prompt = __AGENT_PROMPTS__[agentName];
if (prompt) return prompt;
}
} catch {
}
try {
const agentsDir = (0, import_path2.join)(getPackageDir(), "agents");
const agentPath = (0, import_path2.join)(agentsDir, `${agentName}.md`);
const resolvedPath = (0, import_path2.resolve)(agentPath);
const resolvedAgentsDir = (0, import_path2.resolve)(agentsDir);
const rel = (0, import_path2.relative)(resolvedAgentsDir, resolvedPath);
if (rel.startsWith("..") || (0, import_path2.isAbsolute)(rel)) {
throw new Error(`Invalid agent name: path traversal detected`);
}
const content = (0, import_fs.readFileSync)(agentPath, "utf-8");
return stripFrontmatter(content);
} catch (error) {
const message = error instanceof Error && error.message.includes("Invalid agent name") ? error.message : "Agent prompt file not found";
console.warn(`[loadAgentPrompt] ${message}`);
return `Agent: ${agentName}
Prompt unavailable.`;
}
}
// src/config/loader.ts
var import_fs3 = require("fs");
var import_path5 = require("path");
// src/shared/types.ts
var CANONICAL_TEAM_ROLES = [
"orchestrator",
"planner",
"analyst",
"architect",
"executor",
"debugger",
"critic",
"code-reviewer",
"security-reviewer",
"test-engineer",
"designer",
"writer",
"code-simplifier",
"explore",
"document-specialist"
];
var KNOWN_AGENT_NAMES = [
"omc",
"explore",
"analyst",
"planner",
"architect",
"debugger",
"executor",
"verifier",
"securityReviewer",
"codeReviewer",
"testEngineer",
"designer",
"writer",
"qaTester",
"scientist",
"tracer",
"gitMaster",
"codeSimplifier",
"critic",
"documentSpecialist"
];
// src/utils/paths.ts
var import_path4 = require("path");
var import_fs2 = require("fs");
var import_os2 = require("os");
// src/utils/config-dir.ts
var import_path3 = require("path");
var import_os = require("os");
function stripTrailingSep(p) {
if (!p.endsWith(import_path3.sep)) {
return p;
}
return p === (0, import_path3.parse)(p).root ? p : p.slice(0, -1);
}
function getClaudeConfigDir() {
const home = (0, import_os.homedir)();
const configured = process.env.CLAUDE_CONFIG_DIR?.trim();
if (!configured) {
return stripTrailingSep((0, import_path3.normalize)((0, import_path3.join)(home, ".claude")));
}
if (configured === "~") {
return stripTrailingSep((0, import_path3.normalize)(home));
}
if (configured.startsWith("~/") || configured.startsWith("~\\")) {
return stripTrailingSep((0, import_path3.normalize)((0, import_path3.join)(home, configured.slice(2))));
}
return stripTrailingSep((0, import_path3.normalize)(configured));
}
// src/utils/paths.ts
function getConfigDir() {
if (process.platform === "win32") {
return process.env.APPDATA || (0, import_path4.join)((0, import_os2.homedir)(), "AppData", "Roaming");
}
return process.env.XDG_CONFIG_HOME || (0, import_path4.join)((0, import_os2.homedir)(), ".config");
}
var STALE_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
// src/utils/jsonc.ts
function parseJsonc(content) {
const cleaned = stripJsoncComments(content);
return JSON.parse(cleaned);
}
function stripJsoncComments(content) {
let result = "";
let i = 0;
while (i < content.length) {
if (content[i] === "/" && content[i + 1] === "/") {
while (i < content.length && content[i] !== "\n") {
i++;
}
continue;
}
if (content[i] === "/" && content[i + 1] === "*") {
i += 2;
while (i < content.length && !(content[i] === "*" && content[i + 1] === "/")) {
i++;
}
i += 2;
continue;
}
if (content[i] === '"') {
result += content[i];
i++;
while (i < content.length && content[i] !== '"') {
if (content[i] === "\\") {
result += content[i];
i++;
if (i < content.length) {
result += content[i];
i++;
}
continue;
}
result += content[i];
i++;
}
if (i < content.length) {
result += content[i];
i++;
}
continue;
}
result += content[i];
i++;
}
return result;
}
// src/utils/ssrf-guard.ts
var BLOCKED_HOST_PATTERNS = [
// Exact matches
/^localhost$/i,
/^127\.[0-9]+\.[0-9]+\.[0-9]+$/,
// Loopback
/^10\.[0-9]+\.[0-9]+\.[0-9]+$/,
// Class A private
/^172\.(1[6-9]|2[0-9]|3[0-1])\.[0-9]+\.[0-9]+$/,
// Class B private
/^192\.168\.[0-9]+\.[0-9]+$/,
// Class C private
/^169\.254\.[0-9]+\.[0-9]+$/,
// Link-local
/^(0|22[4-9]|23[0-9])\.[0-9]+\.[0-9]+\.[0-9]+$/,
// Multicast, reserved
/^\[?::1\]?$/,
// IPv6 loopback
/^\[?fc00:/i,
// IPv6 unique local
/^\[?fe80:/i,
// IPv6 link-local
/^\[?::ffff:/i,
// IPv6-mapped IPv4 (all private ranges accessible via this prefix)
/^\[?0{0,4}:{0,2}ffff:/i
// IPv6-mapped IPv4 expanded forms
];
var ALLOWED_SCHEMES = ["https:", "http:"];
function validateUrlForSSRF(urlString) {
if (!urlString || typeof urlString !== "string") {
return { allowed: false, reason: "URL is empty or invalid" };
}
let parsed;
try {
parsed = new URL(urlString);
} catch {
return { allowed: false, reason: "Invalid URL format" };
}
if (!ALLOWED_SCHEMES.includes(parsed.protocol)) {
return { allowed: false, reason: `Protocol '${parsed.protocol}' is not allowed` };
}
const hostname = parsed.hostname.toLowerCase();
for (const pattern of BLOCKED_HOST_PATTERNS) {
if (pattern.test(hostname)) {
return {
allowed: false,
reason: `Hostname '${hostname}' resolves to a blocked internal/private address`
};
}
}
if (/^0x[0-9a-f]+$/i.test(hostname)) {
return {
allowed: false,
reason: `Hostname '${hostname}' looks like a hex-encoded IP address`
};
}
if (/^\d+$/.test(hostname) && hostname.length > 3) {
return {
allowed: false,
reason: `Hostname '${hostname}' looks like a decimal-encoded IP address`
};
}
if (/^0\d+\./.test(hostname)) {
return {
allowed: false,
reason: `Hostname '${hostname}' looks like an octal-encoded IP address`
};
}
if (parsed.username || parsed.password) {
return { allowed: false, reason: "URLs with embedded credentials are not allowed" };
}
const dangerousPaths = [
"/metadata",
"/meta-data",
"/latest/meta-data",
"/computeMetadata"
];
const pathLower = parsed.pathname.toLowerCase();
for (const dangerous of dangerousPaths) {
if (pathLower.startsWith(dangerous)) {
return {
allowed: false,
reason: `Path '${parsed.pathname}' is blocked (cloud metadata access)`
};
}
}
return { allowed: true };
}
function validateAnthropicBaseUrl(urlString) {
const result = validateUrlForSSRF(urlString);
if (!result.allowed) {
return result;
}
let parsed;
try {
parsed = new URL(urlString);
} catch {
return { allowed: false, reason: "Invalid URL" };
}
if (parsed.protocol === "http:") {
console.warn("[SSRF Guard] Warning: Using HTTP instead of HTTPS for ANTHROPIC_BASE_URL");
}
return { allowed: true };
}
// src/config/models.ts
var TIER_ENV_KEYS = {
LOW: [
"OMC_MODEL_LOW",
"CLAUDE_CODE_BEDROCK_HAIKU_MODEL",
"ANTHROPIC_DEFAULT_HAIKU_MODEL"
],
MEDIUM: [
"OMC_MODEL_MEDIUM",
"CLAUDE_CODE_BEDROCK_SONNET_MODEL",
"ANTHROPIC_DEFAULT_SONNET_MODEL"
],
HIGH: [
"OMC_MODEL_HIGH",
"CLAUDE_CODE_BEDROCK_OPUS_MODEL",
"ANTHROPIC_DEFAULT_OPUS_MODEL"
]
};
var CLAUDE_FAMILY_DEFAULTS = {
HAIKU: "claude-haiku-4-5",
SONNET: "claude-sonnet-4-6",
OPUS: "claude-opus-4-7"
};
var BUILTIN_TIER_MODEL_DEFAULTS = {
LOW: CLAUDE_FAMILY_DEFAULTS.HAIKU,
MEDIUM: CLAUDE_FAMILY_DEFAULTS.SONNET,
HIGH: CLAUDE_FAMILY_DEFAULTS.OPUS
};
var CLAUDE_FAMILY_HIGH_VARIANTS = {
HAIKU: `${CLAUDE_FAMILY_DEFAULTS.HAIKU}-high`,
SONNET: `${CLAUDE_FAMILY_DEFAULTS.SONNET}-high`,
OPUS: `${CLAUDE_FAMILY_DEFAULTS.OPUS}-high`
};
var BUILTIN_EXTERNAL_MODEL_DEFAULTS = {
codexModel: "gpt-5.3-codex",
geminiModel: "gemini-3.1-pro-preview"
};
function resolveTierModelFromEnv(tier) {
for (const key of TIER_ENV_KEYS[tier]) {
const value = process.env[key]?.trim();
if (value) {
return value;
}
}
return void 0;
}
function getDefaultModelHigh() {
return resolveTierModelFromEnv("HIGH") || BUILTIN_TIER_MODEL_DEFAULTS.HIGH;
}
function getDefaultModelMedium() {
return resolveTierModelFromEnv("MEDIUM") || BUILTIN_TIER_MODEL_DEFAULTS.MEDIUM;
}
function getDefaultModelLow() {
return resolveTierModelFromEnv("LOW") || BUILTIN_TIER_MODEL_DEFAULTS.LOW;
}
function getDefaultTierModels() {
return {
LOW: getDefaultModelLow(),
MEDIUM: getDefaultModelMedium(),
HIGH: getDefaultModelHigh()
};
}
function resolveClaudeFamily(modelId) {
const lower = modelId.toLowerCase();
if (!lower.includes("claude")) return null;
if (lower.includes("sonnet")) return "SONNET";
if (lower.includes("opus")) return "OPUS";
if (lower.includes("haiku")) return "HAIKU";
return null;
}
function isBedrock() {
if (process.env.CLAUDE_CODE_USE_BEDROCK === "1") {
return true;
}
const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
if (modelId && /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
return true;
}
if (modelId && /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) && /:(inference-profile|application-inference-profile)\//i.test(modelId) && modelId.toLowerCase().includes("claude")) {
return true;
}
return false;
}
function isProviderSpecificModelId(modelId) {
if (/^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
return true;
}
if (/^arn:aws(-[^:]+)?:bedrock:/i.test(modelId)) {
return true;
}
if (modelId.toLowerCase().startsWith("vertex_ai/")) {
return true;
}
return false;
}
function isVertexAI() {
if (process.env.CLAUDE_CODE_USE_VERTEX === "1") {
return true;
}
const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
if (modelId && modelId.toLowerCase().startsWith("vertex_ai/")) {
return true;
}
return false;
}
function isNonClaudeProvider() {
if (process.env.OMC_ROUTING_FORCE_INHERIT === "true") {
return true;
}
if (isBedrock()) {
return true;
}
if (isVertexAI()) {
return true;
}
const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
if (modelId && !modelId.toLowerCase().includes("claude")) {
return true;
}
const baseUrl = process.env.ANTHROPIC_BASE_URL || "";
if (baseUrl) {
const validation = validateAnthropicBaseUrl(baseUrl);
if (!validation.allowed) {
console.error(`[SSRF Guard] Rejecting ANTHROPIC_BASE_URL: ${validation.reason}`);
return true;
}
if (!baseUrl.includes("anthropic.com")) {
return true;
}
}
return false;
}
// src/features/delegation-routing/types.ts
var DEPRECATED_ROLE_ALIASES = {
researcher: "document-specialist",
"tdd-guide": "test-engineer",
"api-reviewer": "code-reviewer",
"performance-reviewer": "code-reviewer",
"dependency-expert": "document-specialist",
"quality-strategist": "code-reviewer",
vision: "document-specialist",
// Consolidated agent aliases (agent consolidation PR)
"quality-reviewer": "code-reviewer",
"deep-executor": "executor",
"build-fixer": "debugger",
"harsh-critic": "critic",
// User-friendly short alias for /team role routing (plan AC-4)
reviewer: "code-reviewer"
};
function normalizeDelegationRole(role) {
return DEPRECATED_ROLE_ALIASES[role] ?? role;
}
// src/config/loader.ts
function buildDefaultConfig() {
const defaultTierModels = getDefaultTierModels();
return {
agents: {
omc: { model: defaultTierModels.HIGH },
explore: { model: defaultTierModels.LOW },
analyst: { model: defaultTierModels.HIGH },
planner: { model: defaultTierModels.HIGH },
architect: { model: defaultTierModels.HIGH },
debugger: { model: defaultTierModels.MEDIUM },
executor: { model: defaultTierModels.MEDIUM },
verifier: { model: defaultTierModels.MEDIUM },
securityReviewer: { model: defaultTierModels.MEDIUM },
codeReviewer: { model: defaultTierModels.HIGH },
testEngineer: { model: defaultTierModels.MEDIUM },
designer: { model: defaultTierModels.MEDIUM },
writer: { model: defaultTierModels.LOW },
qaTester: { model: defaultTierModels.MEDIUM },
scientist: { model: defaultTierModels.MEDIUM },
tracer: { model: defaultTierModels.MEDIUM },
gitMaster: { model: defaultTierModels.MEDIUM },
codeSimplifier: { model: defaultTierModels.HIGH },
critic: { model: defaultTierModels.HIGH },
documentSpecialist: { model: defaultTierModels.MEDIUM }
},
features: {
parallelExecution: true,
lspTools: true,
// Real LSP integration with language servers
astTools: true,
// Real AST tools using ast-grep
continuationEnforcement: true,
autoContextInjection: true
},
mcpServers: {
exa: { enabled: true },
context7: { enabled: true }
},
companyContext: {
onError: "warn"
},
permissions: {
allowBash: true,
allowEdit: true,
allowWrite: true,
maxBackgroundTasks: 5
},
magicKeywords: {
ultrawork: ["ultrawork", "ulw", "uw"],
search: ["search", "find", "locate"],
analyze: ["analyze", "investigate", "examine"],
ultrathink: ["ultrathink", "think", "reason", "ponder"]
},
// Intelligent model routing configuration
routing: {
enabled: true,
defaultTier: "MEDIUM",
forceInherit: false,
escalationEnabled: true,
maxEscalations: 2,
tierModels: { ...defaultTierModels },
agentOverrides: {
architect: {
tier: "HIGH",
reason: "Advisory agent requires deep reasoning"
},
planner: {
tier: "HIGH",
reason: "Strategic planning requires deep reasoning"
},
critic: {
tier: "HIGH",
reason: "Critical review requires deep reasoning"
},
analyst: {
tier: "HIGH",
reason: "Pre-planning analysis requires deep reasoning"
},
explore: { tier: "LOW", reason: "Exploration is search-focused" },
writer: { tier: "LOW", reason: "Documentation is straightforward" }
},
escalationKeywords: [
"critical",
"production",
"urgent",
"security",
"breaking",
"architecture",
"refactor",
"redesign",
"root cause"
],
simplificationKeywords: [
"find",
"list",
"show",
"where",
"search",
"locate",
"grep"
]
},
// External models configuration (Codex, Gemini)
// Static defaults only — env var overrides applied in loadEnvConfig()
externalModels: {
defaults: {
codexModel: BUILTIN_EXTERNAL_MODEL_DEFAULTS.codexModel,
geminiModel: BUILTIN_EXTERNAL_MODEL_DEFAULTS.geminiModel
},
fallbackPolicy: {
onModelFailure: "provider_chain",
allowCrossProvider: false,
crossProviderOrder: ["codex", "gemini"]
}
},
// Delegation routing configuration (opt-in feature for external model routing)
delegationRouting: {
enabled: false,
defaultProvider: "claude",
roles: {}
},
// /team role routing (Option E — /team-scoped per-role provider & model)
// Empty defaults: zero behavior change until user opts in.
team: {
ops: {},
roleRouting: {}
},
planOutput: {
directory: ".omc/plans",
filenameTemplate: "{{name}}.md"
},
teleport: {
symlinkNodeModules: true
},
startupCodebaseMap: {
enabled: true,
maxFiles: 200,
maxDepth: 4
},
taskSizeDetection: {
enabled: true,
smallWordLimit: 50,
largeWordLimit: 200,
suppressHeavyModesForSmallTasks: true
},
promptPrerequisites: {
enabled: true,
sectionNames: {
memory: ["M\xC9MOIRE", "MEMOIRE", "MEMORY"],
skills: ["SKILLS"],
verifyFirst: ["VERIFY-FIRST", "VERIFY FIRST", "VERIFY_FIRST"],
context: ["CONTEXT"]
},
blockingTools: ["Edit", "MultiEdit", "Write", "Agent", "Task"],
executionKeywords: ["ralph", "ultrawork", "autopilot"]
}
};
}
var DEFAULT_CONFIG = buildDefaultConfig();
function getConfigPaths() {
const userConfigDir = getConfigDir();
return {
user: (0, import_path5.join)(userConfigDir, "claude-omc", "config.jsonc"),
project: (0, import_path5.join)(process.cwd(), ".claude", "omc.jsonc")
};
}
function loadJsoncFile(path4) {
if (!(0, import_fs3.existsSync)(path4)) {
return null;
}
try {
const content = (0, import_fs3.readFileSync)(path4, "utf-8");
const result = parseJsonc(content);
return result;
} catch (error) {
console.error(`Error loading config from ${path4}:`, error);
return null;
}
}
function deepMerge(target, source) {
const result = { ...target };
const mutableResult = result;
for (const key of Object.keys(source)) {
if (key === "__proto__" || key === "constructor" || key === "prototype")
continue;
const sourceValue = source[key];
const targetValue = mutableResult[key];
if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) {
mutableResult[key] = deepMerge(
targetValue,
sourceValue
);
} else if (sourceValue !== void 0) {
mutableResult[key] = sourceValue;
}
}
return result;
}
function loadEnvConfig() {
const config = {};
if (process.env.EXA_API_KEY) {
config.mcpServers = {
...config.mcpServers,
exa: { enabled: true, apiKey: process.env.EXA_API_KEY }
};
}
if (process.env.OMC_PARALLEL_EXECUTION !== void 0) {
config.features = {
...config.features,
parallelExecution: process.env.OMC_PARALLEL_EXECUTION === "true"
};
}
if (process.env.OMC_LSP_TOOLS !== void 0) {
config.features = {
...config.features,
lspTools: process.env.OMC_LSP_TOOLS === "true"
};
}
if (process.env.OMC_MAX_BACKGROUND_TASKS) {
const maxTasks = parseInt(process.env.OMC_MAX_BACKGROUND_TASKS, 10);
if (!isNaN(maxTasks)) {
config.permissions = {
...config.permissions,
maxBackgroundTasks: maxTasks
};
}
}
if (process.env.OMC_ROUTING_ENABLED !== void 0) {
config.routing = {
...config.routing,
enabled: process.env.OMC_ROUTING_ENABLED === "true"
};
}
if (process.env.OMC_ROUTING_FORCE_INHERIT !== void 0) {
config.routing = {
...config.routing,
forceInherit: process.env.OMC_ROUTING_FORCE_INHERIT === "true"
};
}
if (process.env.OMC_ROUTING_DEFAULT_TIER) {
const tier = process.env.OMC_ROUTING_DEFAULT_TIER.toUpperCase();
if (tier === "LOW" || tier === "MEDIUM" || tier === "HIGH") {
config.routing = {
...config.routing,
defaultTier: tier
};
}
}
const aliasKeys = ["HAIKU", "SONNET", "OPUS"];
const modelAliases = {};
for (const key of aliasKeys) {
const envVal = process.env[`OMC_MODEL_ALIAS_${key}`];
if (envVal) {
const lower = key.toLowerCase();
modelAliases[lower] = envVal.toLowerCase();
}
}
if (Object.keys(modelAliases).length > 0) {
config.routing = {
...config.routing,
modelAliases
};
}
if (process.env.OMC_ESCALATION_ENABLED !== void 0) {
config.routing = {
...config.routing,
escalationEnabled: process.env.OMC_ESCALATION_ENABLED === "true"
};
}
const externalModelsDefaults = {};
if (process.env.OMC_EXTERNAL_MODELS_DEFAULT_PROVIDER) {
const provider = process.env.OMC_EXTERNAL_MODELS_DEFAULT_PROVIDER;
if (provider === "codex" || provider === "gemini") {
externalModelsDefaults.provider = provider;
}
}
if (process.env.OMC_EXTERNAL_MODELS_DEFAULT_CODEX_MODEL) {
externalModelsDefaults.codexModel = process.env.OMC_EXTERNAL_MODELS_DEFAULT_CODEX_MODEL;
} else if (process.env.OMC_CODEX_DEFAULT_MODEL) {
externalModelsDefaults.codexModel = process.env.OMC_CODEX_DEFAULT_MODEL;
}
if (process.env.OMC_EXTERNAL_MODELS_DEFAULT_GEMINI_MODEL) {
externalModelsDefaults.geminiModel = process.env.OMC_EXTERNAL_MODELS_DEFAULT_GEMINI_MODEL;
} else if (process.env.OMC_GEMINI_DEFAULT_MODEL) {
externalModelsDefaults.geminiModel = process.env.OMC_GEMINI_DEFAULT_MODEL;
}
const externalModelsFallback = {
onModelFailure: "provider_chain"
};
if (process.env.OMC_EXTERNAL_MODELS_FALLBACK_POLICY) {
const policy = process.env.OMC_EXTERNAL_MODELS_FALLBACK_POLICY;
if (policy === "provider_chain" || policy === "cross_provider" || policy === "claude_only") {
externalModelsFallback.onModelFailure = policy;
}
}
if (Object.keys(externalModelsDefaults).length > 0 || externalModelsFallback.onModelFailure !== "provider_chain") {
config.externalModels = {
defaults: externalModelsDefaults,
fallbackPolicy: externalModelsFallback
};
}
if (process.env.OMC_DELEGATION_ROUTING_ENABLED !== void 0) {
config.delegationRouting = {
...config.delegationRouting,
enabled: process.env.OMC_DELEGATION_ROUTING_ENABLED === "true"
};
}
if (process.env.OMC_DELEGATION_ROUTING_DEFAULT_PROVIDER) {
const provider = process.env.OMC_DELEGATION_ROUTING_DEFAULT_PROVIDER;
if (["claude", "codex", "gemini"].includes(provider)) {
config.delegationRouting = {
...config.delegationRouting,
defaultProvider: provider
};
}
}
const teamRoleOverrides = parseTeamRoleOverridesFromEnv();
if (teamRoleOverrides) {
config.team = {
...config.team,
roleRouting: {
...config.team?.roleRouting,
...teamRoleOverrides
}
};
}
return config;
}
function warnOnDeprecatedDelegationRouting(config) {
const deprecatedProviders = /* @__PURE__ */ new Set();
const defaultProvider = config.delegationRouting?.defaultProvider;
if (defaultProvider === "codex" || defaultProvider === "gemini") {
deprecatedProviders.add(defaultProvider);
}
const roles = config.delegationRouting?.roles ?? {};
for (const route of Object.values(roles)) {
const provider = route?.provider;
if (provider === "codex" || provider === "gemini") {
deprecatedProviders.add(provider);
}
}
if (deprecatedProviders.size === 0) {
return;
}
console.warn(
"[OMC] delegationRouting to Codex/Gemini is deprecated and falls back to Claude Task. Use /team for Codex/Gemini CLI workers instead."
);
}
var CANONICAL_TEAM_ROLE_SET = new Set(CANONICAL_TEAM_ROLES);
var KNOWN_AGENT_NAME_SET = new Set(KNOWN_AGENT_NAMES);
var TEAM_ROLE_PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini"]);
var TEAM_ROLE_TIERS = /* @__PURE__ */ new Set(["HIGH", "MEDIUM", "LOW"]);
function validateTeamConfig(config) {
const team = config.team;
if (!team || typeof team !== "object") return;
const ops = team.ops;
if (ops && typeof ops === "object") {
if (ops.defaultAgentType !== void 0) {
if (typeof ops.defaultAgentType !== "string" || !TEAM_ROLE_PROVIDERS.has(ops.defaultAgentType)) {
throw new Error(
`[OMC] team.ops.defaultAgentType: invalid value "${String(ops.defaultAgentType)}". Allowed: ${[...TEAM_ROLE_PROVIDERS].join(", ")}`
);
}
}
}
const roleRouting = team.roleRouting;
if (!roleRouting || typeof roleRouting !== "object") return;
for (const [rawRoleKey, rawSpec] of Object.entries(roleRouting)) {
const normalized = normalizeDelegationRole(rawRoleKey);
if (!CANONICAL_TEAM_ROLE_SET.has(normalized)) {
throw new Error(
`[OMC] team.roleRouting: unknown role "${rawRoleKey}". Allowed roles: ${[...CANONICAL_TEAM_ROLE_SET].join(", ")}`
);
}
if (!rawSpec || typeof rawSpec !== "object" || Array.isArray(rawSpec)) {
throw new Error(
`[OMC] team.roleRouting.${rawRoleKey}: must be an object, got ${Array.isArray(rawSpec) ? "array" : typeof rawSpec}`
);
}
const spec = rawSpec;
if (normalized === "orchestrator") {
for (const key of Object.keys(spec)) {
if (key !== "model") {
throw new Error(
`[OMC] team.roleRouting.orchestrator: key "${key}" is not allowed (orchestrator is pinned to claude; only "model" is configurable)`
);
}
}
if (spec.model !== void 0 && !isValidModelValue(spec.model)) {
throw new Error(
`[OMC] team.roleRouting.orchestrator.model: must be a tier name (HIGH|MEDIUM|LOW) or model ID string, got ${typeof spec.model}`
);
}
continue;
}
if (spec.provider !== void 0) {
if (typeof spec.provider !== "string" || !TEAM_ROLE_PROVIDERS.has(spec.provider)) {
throw new Error(
`[OMC] team.roleRouting.${rawRoleKey}.provider: invalid value "${String(spec.provider)}". Allowed: ${[...TEAM_ROLE_PROVIDERS].join(", ")}`
);
}
}
if (spec.model !== void 0 && !isValidModelValue(spec.model)) {
throw new Error(
`[OMC] team.roleRouting.${rawRoleKey}.model: must be a tier name (HIGH|MEDIUM|LOW) or a non-empty model ID string`
);
}
if (spec.agent !== void 0) {
if (typeof spec.agent !== "string" || !KNOWN_AGENT_NAME_SET.has(spec.agent)) {
throw new Error(
`[OMC] team.roleRouting.${rawRoleKey}.agent: unknown agent "${String(spec.agent)}". Allowed: ${[...KNOWN_AGENT_NAME_SET].join(", ")}`
);
}
}
}
}
function isValidModelValue(value) {
if (typeof value !== "string") return false;
if (value.length === 0) return false;
return TEAM_ROLE_TIERS.has(value) || value.length > 0;
}
function parseTeamRoleOverridesFromEnv() {
const raw = process.env.OMC_TEAM_ROLE_OVERRIDES;
if (!raw) return void 0;
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
console.warn(
"[OMC] OMC_TEAM_ROLE_OVERRIDES: expected a JSON object; ignoring."
);
return void 0;
}
return parsed;
} catch (err) {
console.warn(
`[OMC] OMC_TEAM_ROLE_OVERRIDES: invalid JSON, ignoring (${err.message})`
);
return void 0;
}
}
function loadConfig() {
const paths = getConfigPaths();
let config = buildDefaultConfig();
const userConfig = loadJsoncFile(paths.user);
if (userConfig) {
config = deepMerge(config, userConfig);
}
const projectConfig = loadJsoncFile(paths.project);
if (projectConfig) {
config = deepMerge(config, projectConfig);
}
const envConfig = loadEnvConfig();
config = deepMerge(config, envConfig);
if (config.routing?.forceInherit !== true && process.env.OMC_ROUTING_FORCE_INHERIT === void 0 && isNonClaudeProvider()) {
config.routing = {
...config.routing,
forceInherit: true
};
}
warnOnDeprecatedDelegationRouting(config);
validateTeamConfig(config);
return config;
}
// src/agents/architect.ts
var ARCHITECT_PROMPT_METADATA = {
category: "advisor",
cost: "EXPENSIVE",
promptAlias: "architect",
triggers: [
{ domain: "Architecture decisions", trigger: "Multi-system tradeoffs, unfamiliar patterns" },
{ domain: "Self-review", trigger: "After completing significant implementation" },
{ domain: "Hard debugging", trigger: "After 2+ failed fix attempts" }
],
useWhen: [
"Complex architecture design",
"After completing significant work",
"2+ failed fix attempts",
"Unfamiliar code patterns",
"Security/performance concerns",
"Multi-system tradeoffs"
],
avoidWhen: [
"Simple file operations (use direct tools)",
"First attempt at any fix (try yourself first)",
"Questions answerable from code you've read",
"Trivial decisions (variable names, formatting)",
"Things you can infer from existing code patterns"
]
};
var architectAgent = {
name: "architect",
description: "Read-only consultation agent. High-IQ reasoning specialist for debugging hard problems and high-difficulty architecture design.",
prompt: loadAgentPrompt("architect"),
model: "opus",
defaultModel: "opus",
metadata: ARCHITECT_PROMPT_METADATA
};
// src/agents/designer.ts
var FRONTEND_ENGINEER_PROMPT_METADATA = {
category: "specialist",
cost: "CHEAP",
promptAlias: "designer",
triggers: [
{
domain: "UI/UX",
trigger: "Visual changes, styling, components, accessibility"
},
{
domain: "Design",
trigger: "Layout, animations, responsive design"
}
],
useWhen: [
"Visual styling or layout changes",
"Component design or refactoring",
"Animation implementation",
"Accessibility improvements",
"Responsive design work"
],
avoidWhen: [
"Pure logic changes in frontend files",
"Backend/API work",
"Non-visual refactoring"
]
};
var designerAgent = {
name: "designer",
description: `Designer-turned-developer who crafts stunning UI/UX even without design mockups. Use for VISUAL changes only (styling, layout, animation). Pure logic changes in frontend files should be handled directly.`,
prompt: loadAgentPrompt("designer"),
model: "sonnet",
defaultModel: "sonnet",
metadata: FRONTEND_ENGINEER_PROMPT_METADATA
};
// src/agents/writer.ts
var DOCUMENT_WRITER_PROMPT_METADATA = {
category: "specialist",
cost: "FREE",
promptAlias: "writer",
triggers: [
{
domain: "Documentation",
trigger: "README, API docs, guides, comments"
}
],
useWhen: [
"Creating or updating README files",
"Writing API documentation",
"Creating user guides or tutorials",
"Adding code comments or JSDoc",
"Architecture documentation"
],
avoidWhen: [
"Code implementation tasks",
"Bug fixes",
"Non-documentation tasks"
]
};
var writerAgent = {
name: "writer",
description: `Technical writer who crafts clear, comprehensive documentation. Specializes in README files, API docs, architecture docs, and user guides.`,
prompt: loadAgentPrompt("writer"),
model: "haiku",
defaultModel: "haiku",
metadata: DOCUMENT_WRITER_PROMPT_METADATA
};
// src/agents/critic.ts
var CRITIC_PROMPT_METADATA = {
category: "reviewer",
cost: "EXPENSIVE",
promptAlias: "critic",
triggers: [
{
domain: "Plan Review",
trigger: "Evaluating work plans before execution"
}
],
useWhen: [
"After planner creates a work plan",
"Before executing a complex plan",
"When plan quality validation is needed",
"To catch gaps before implementation"
],
avoidWhen: [
"Simple, straightforward tasks",
"When no plan exists to review",
"During implementation phase"
]
};
var criticAgent = {
name: "critic",
description: `Expert reviewer for evaluating work plans against rigorous clarity, verifiability, and completeness standards. Use after planner creates a work plan to validate it before execution.`,
prompt: loadAgentPrompt("critic"),
model: "opus",
defaultModel: "opus",
metadata: CRITIC_PROMPT_METADATA
};
// src/agents/analyst.ts
var ANALYST_PROMPT_METADATA = {
category: "planner",
cost: "EXPENSIVE",
promptAlias: "analyst",
triggers: [
{
domain: "Pre-Planning",
trigger: "Hidden requirements, edge cases, risk analysis"
}
],
useWhen: [
"Before creating a work plan",
"When requirements seem incomplete",
"To identify hidden assumptions",
"Risk analysis before implementation",
"Scope validation"
],
avoidWhen: [
"Simple, well-defined tasks",
"During implementation phase",
"When plan already reviewed"
]
};
var analystAgent = {
name: "analyst",
description: `Pre-planning consultant that analyzes requests before implementation to identify hidden requirements, edge cases, and potential risks. Use before creating a work plan.`,
prompt: loadAgentPrompt("analyst"),
model: "opus",
defaultModel: "opus",
metadata: ANALYST_PROMPT_METADATA
};
// src/agents/executor.ts
var EXECUTOR_PROMPT_METADATA = {
category: "specialist",
cost: "CHEAP",
promptAlias: "Junior",
triggers: [
{ domain: "Direct implementation", trigger: "Single-file changes, focused tasks" },
{ domain: "Bug fixes", trigger: "Clear, scoped fixes" },
{ domain: "Small features", trigger: "Well-defined, isolated work" }
],
useWhen: [
"Direct, focused implementation tasks",
"Single-file or few-file changes",
"When delegation overhead isn't worth it",
"Clear, well-scoped work items"
],
avoidWhen: [
"Multi-file refactoring (use orchestrator)",
"Tasks requiring research (use explore/document-specialist first)",
"Complex decisions (consult architect)"
]
};
var executorAgent = {
name: "executor",
description: "Focused task executor. Execute tasks directly. NEVER delegate or spawn other agents. Same discipline as OMC, no delegation.",
prompt: loadAgentPrompt("executor"),
model: "sonnet",
defaultModel: "sonnet",
metadata: EXECUTOR_PROMPT_METADATA
};
// src/agents/planner.ts
var PLANNER_PROMPT_METADATA = {
category: "planner",
cost: "EXPENSIVE",
promptAlias: "planner",
triggers: [
{
domain: "Strategic Planning",
trigger: "Comprehensive work plans, interview-style consultation"
}
],
useWhen: [
"Complex features requiring planning",
"When requirements need clarification through interview",
"Creating comprehensive work plans",
"Before large implementation efforts"
],
avoidWhen: [
"Simple, straightforward tasks",
"When implementation should just start",
"When a plan already exists"
]
};
var plannerAgent = {
name: "planner",
description: `Strategic planning consultant. Interviews users to understand requirements, then creates comprehensive work plans. NEVER implements - only plans.`,
prompt: loadAgentPrompt("planner"),
model: "opus",
defaultModel: "opus",
metadata: PLANNER_PROMPT_METADATA
};
// src/agents/qa-tester.ts
var QA_TESTER_PROMPT_METADATA = {
category: "specialist",
cost: "CHEAP",
promptAlias: "QATester",
triggers: [
{ domain: "CLI testing", trigger: "Testing command-line applications" },
{ domain: "Service testing", trigger: "Starting and testing background services" },
{ domain: "Integration testing", trigger: "End-to-end CLI workflow verification" },
{ domain: "Interactive testing", trigger: "Testing applications requiring user input" }
],
useWhen: [
"Testing CLI applications that need interactive input",
"Starting background services and verifying their behavior",
"Running end-to-end tests on command-line tools",
"Testing applications that produce streaming output",
"Verifying service startup and shutdown behavior"
],
avoidWhen: [
"Unit testing (use standard test runners)",
"API testing without CLI interface (use curl/httpie directly)",
"Static code analysis (use architect or explore)"
]
};
var qaTesterAgent = {
name: "qa-tester",
description: "Interactive CLI testing specialist using tmux. Tests CLI applications, background services, and interactive tools. Manages test sessions, sends commands, verifies output, and ensures cleanup.",
prompt: loadAgentPrompt("qa-tester"),
model: "sonnet",
defaultModel: "sonnet",
metadata: QA_TESTER_PROMPT_METADATA
};
// src/agents/scientist.ts
var SCIENTIST_PROMPT_METADATA = {
category: "specialist",
cost: "CHEAP",
promptAlias: "scientist",
triggers: [
{ domain: "Data analysis", trigger: "Analyzing datasets and computing statistics" },
{ domain: "Research execution", trigger: "Running data experiments and generating findings" },
{ domain: "Python data work", trigger: "Using pandas, numpy, scipy for data tasks" },
{ domain: "EDA", trigger: "Exploratory data analysis on files" },
{ domain: "Hypothesis testing", trigger: "Statistical tests with confidence intervals and effect sizes" },
{ domain: "Research stages", trigger: "Multi-stage analysis with structured markers" }
],
useWhen: [
"Analyzing CSV, JSON, Parquet, or other data files",
"Computing descriptive statistics or aggregations",
"Performing exploratory data analysis (EDA)",
"Generating data-driven findings and insights",
"Simple ML tasks like clustering or regression",
"Data transformations and feature engineering",
"Generating data analysis reports with visualizations",
"Hypothesis testing with statistical evidence markers",
"Research stages with [STAGE:*] markers for orchestration"
],
avoidWhen: [
"Researching external documentation or APIs (use document-specialist)",
"Implementing production code features (use executor)",
"Architecture or system design questions (use architect)",
"No data files to analyze - just theoretical questions",
"Web scraping or external data fetching (use document-specialist)"
]
};
var scientistAgent = {
name: "scientist",
description: "Data analysis and research execution specialist. Executes Python code for EDA, statistical analysis, and generating data-driven findings. Works with CSV, JSON, Parquet files using pandas, numpy, scipy.",
prompt: loadAgentPrompt("scientist"),
model: "sonnet",
defaultModel: "sonnet",
metadata: SCIENTIST_PROMPT_METADATA
};
// src/agents/explore.ts
var EXPLORE_PROMPT_METADATA = {
category: "exploration",
cost: "CHEAP",
promptAlias: "Explore",
triggers: [
{ domain: "Internal codebase search", trigger: "Finding implementations, patterns, files" },
{ domain: "Project structure", trigger: "Understanding code organization" },
{ domain: "Code discovery", trigger: "Locating specific code by pattern" }
],
useWhen: [
"Finding files by pattern or name",
"Searching for implementations in current project",
"Understanding project structure",
"Locating code by content or pattern",
"Quick codebase exploration"
],
avoidWhen: [
"External documentation, literature, or academic paper lookup (use document-specialist)",
"Database/reference/manual lookups outside the current project (use document-specialist)",
"GitHub/npm package research (use document-specialist)",
"Complex architectural analysis (use architect)",
"When you already know the file location"
]
};
var exploreAgent = {
name: "explore",
description: "Fast codebase exploration and pattern search. Use for finding files, understanding structure, locating implementations. Searches INTERNAL codebase only; external docs, literature, papers, and reference databases belong to document-specialist.",
prompt: loadAgentPrompt("explore"),
model: "haiku",
defaultModel: "haiku",
metadata: EXPLORE_PROMPT_METADATA
};
// src/agents/tracer.ts
var TRACER_PROMPT_METADATA = {
category: "advisor",
cost: "EXPENSIVE",
promptAlias: "tracer",
triggers: [
{ domain: "Causal tracing", trigger: "Why did this happen? Which explanation best fits the evidence?" },
{ domain: "Forensic analysis", trigger: "Observed output, artifact, or behavior needs ranked explanations" },
{ domain: "Evidence-driven uncertainty reduction", trigger: "Need competing hypotheses and the next best probe" }
],
useWhen: [
"Tracing ambiguous runtime behavior, regressions, or orchestration outcomes",
"Ranking competing explanations for an observed result",
"Separating observation, evidence, and inference",
"Explaining performance, architecture, scientific, or configuration outcomes",
"Identifying the next probe that would collapse uncertainty fastest"
],
avoidWhen: [
"The task is pure implementation or fixing (use executor/debugger)",
"The task is a generic summary without causal analysis",
"A single-file code search is enough (use explore)",
"You already have decisive evidence and only need execution"
]
};
var tracerAgent = {
name: "tracer",
description: "Evidence-driven causal tracing specialist. Explains observed outcomes using competing hypotheses, evidence for and against, uncertainty tracking, and next-probe recommendations.",
prompt: loadAgentPrompt("tracer"),
model: "sonnet",
defaultModel: "sonnet",
metadata: TRACER_PROMPT_METADATA
};
// src/agents/document-specialist.ts
var DOCUMENT_SPECIALIST_PROMPT_METADATA = {
category: "exploration",
cost: "CHEAP",
promptAlias: "document-specialist",
triggers: [
{
domain: "Project documentation",
trigger: "README, docs/, migration guides, local references"
},
{
domain: "External documentation",
trigger: "API references, official docs"
},
{
domain: "API/framework correctness",
trigger: "Context Hub / chub first when available; curated backend fallback otherwise"
},
{
domain: "OSS implementations",
trigger: "GitHub examples, package source"
},
{
domain: "Best practices",
trigger: "Community patterns, recommendations"
},
{
domain: "Literature and reference research",
trigger: "Academic papers, manuals, reference databases"
}
],
useWhen: [
"Checking README/docs/local reference files before broader research",
"Looking up official documentation",
"Using Context Hub / chub (or another curated docs backend) for external API/framework correctness when available",
"Finding GitHub examples",
"Researching npm/pip packages",
"Stack Overflow solutions",
"External API references",
"Searching external literature or academic papers",
"Looking up manuals, databases, or reference material outside the current project"
],
avoidWhen: [
"Internal codebase implementation search (use explore)",
"Current project source files when the task is code discovery rather than documentation lookup (use explore)",
"When you already have the information"
]
};
var documentSpecialistAgent = {
name: "document-specialist",
description: "Document Specialist for documentation research and reference finding. Use for local repo docs, official docs, Context Hub / chub or other curated docs backends for API/framework correctness, GitHub examples, OSS implementations, external literature, academic papers, and reference/database lookups. Avoid internal implementation search; use explore for code discovery.",
prompt: loadAgentPrompt("document-specialist"),
model: "sonnet",
defaultModel: "sonnet",
metadata: DOCUMENT_SPECIALIST_PROMPT_METADATA
};
// src/agents/definitions.ts
var debuggerAgent = {
name: "debugger",
description: "Root-cause analysis, regression isolation, failure diagnosis (Sonnet).",
prompt: loadAgentPrompt("debugger"),
model: "sonnet",
defaultModel: "sonnet"
};
var verifierAgent = {
name: "verifier",
description: "Completion evidence, claim validation, test adequacy (Sonnet).",
prompt: loadAgentPrompt("verifier"),
model: "sonnet",
defaultModel: "sonnet"
};
var testEngineerAgent = {
name: "test-engineer",
description: "Test strategy, coverage, flaky test hardening (Sonnet).",
prompt: loadAgentPrompt("test-engineer"),
model: "sonnet",
defaultModel: "sonnet"
};
var securityReviewerAgent = {
name: "security-reviewer",
description: "Security vulnerability detection specialist (Sonnet). Use for security audits and OWASP detection.",
prompt: loadAgentPrompt("security-reviewer"),
model: "sonnet",
defaultModel: "sonnet"
};
var codeReviewerAgent = {
name: "code-reviewer",
description: "Expert code review specialist (Opus). Use for comprehensive code quality review.",
prompt: loadAgentPrompt("code-reviewer"),
model: "opus",
defaultModel: "opus"
};
var gitMasterAgent = {
name: "git-master",
description: "Git expert for atomic commits, rebasing, and history management with style detection",
prompt: loadAgentPrompt("git-master"),
model: "sonnet",
defaultModel: "sonnet"
};
var codeSimplifierAgent = {
name: "code-simplifier",
description: "Simplifies and refines code for clarity, consistency, and maintainability (Opus).",
prompt: loadAgentPrompt("code-simplifier"),
model: "opus",
defaultModel: "opus"
};
// src/features/delegation-enforcer.ts
var FAMILY_TO_ALIAS = {
SONNET: "sonnet",
OPUS: "opus",
HAIKU: "haiku"
};
function normalizeToCcAlias(model) {
const family = resolveClaudeFamily(model);
return family ? FAMILY_TO_ALIAS[family] ?? model : model;
}
// src/lib/security-config.ts
var import_fs4 = require("fs");
var import_path6 = require("path");
var DEFAULTS = {
restrictToolPaths: false,
pythonSandbox: false,
disableProjectSkills: false,
disableAutoUpdate: false,
hardMaxIterations: 500,
disableRemoteMcp: false,
disableExternalLLM: false
};
var STRICT_OVERRIDES = {
restrictToolPaths: true,
pythonSandbox: true,
disableProjectSkills: true,
disableAutoUpdate: true,
hardMaxIterations: 200,
disableRemoteMcp: true,
disableExternalLLM: true
};
var cachedConfig = null;
function loadSecurityFromConfigFiles() {
const paths = [
(0, import_path6.join)(process.cwd(), ".claude", "omc.jsonc"),
(0, import_path6.join)(getConfigDir(), "claude-omc", "config.jsonc")
];
for (const configPath of paths) {
if (!(0, import_fs4.existsSync)(configPath)) continue;
try {
const content = (0, import_fs4.readFileSync)(configPath, "utf-8");
const parsed = parseJsonc(content);
if (parsed?.security && typeof parsed.security === "object") {
return parsed.security;
}
} catch {
}
}
return {};
}
function getSecurityConfig() {
if (cachedConfig) return cachedConfig;
const isStrict = process.env.OMC_SECURITY === "strict";
const base = isStrict ? { ...STRICT_OVERRIDES } : { ...DEFAULTS };
const fileOverrides = loadSecurityFromConfigFiles();
if (isStrict) {
cachedConfig = {
restrictToolPaths: base.restrictToolPaths || (fileOverrides.restrictToolPaths ?? false),
pythonSandbox: base.pythonSandbox || (fileOverrides.pythonSandbox ?? false),
disableProjectSkills: base.disableProjectSkills || (fileOverrides.disableProjectSkills ?? false),
disableAutoUpdate: base.disableAutoUpdate || (fileOverrides.disableAutoUpdate ?? false),
disableRemoteMcp: base.disableRemoteMcp || (fileOverrides.disableRemoteMcp ?? false),
disableExternalLLM: base.disableExternalLLM || (fileOverrides.disableExternalLLM ?? false),
hardMaxIterations: Math.min(base.hardMaxIterations, typeof fileOverrides.hardMaxIterations === "number" && fileOverrides.hardMaxIterations > 0 ? fileOverrides.hardMaxIterations : base.hardMaxIterations)
};
} else {
cachedConfig = {
restrictToolPaths: fileOverrides.restrictToolPaths ?? base.restrictToolPaths,
pythonSandbox: fileOverrides.pythonSandbox ?? base.pythonSandbox,
disableProjectSkills: fileOverrides.disableProjectSkills ?? base.disableProjectSkills,
disableAutoUpdate: fileOverrides.disableAutoUpdate ?? base.disableAutoUpdate,
disableRemoteMcp: fileOverrides.disableRemoteMcp ?? base.disableRemoteMcp,
disableExternalLLM: fileOverrides.disableExternalLLM ?? base.disableExternalLLM,
hardMaxIterations: fileOverrides.hardMaxIterations ?? base.hardMaxIterations
};
}
return cachedConfig;
}
function isExternalLLMDisabled() {
return getSecurityConfig().disableExternalLLM;
}
// src/team/model-contract.ts
var resolvedPathCache = /* @__PURE__ */ new Map();
var UNTRUSTED_PATH_PATTERNS = [
/^\/tmp(\/|$)/,
/^\/var\/tmp(\/|$)/,
/^\/dev\/shm(\/|$)/
];
function getTrustedPrefixes() {
const trusted = [
"/usr/local/bin",
"/usr/bin",
"/opt/homebrew/"
];
const home = process.env.HOME;
if (home) {
trusted.push(`${home}/.local/bin`);
trusted.push(`${home}/.nvm/`);
trusted.push(`${home}/.cargo/bin`);
}
const custom = (process.env.OMC_TRUSTED_CLI_DIRS ?? "").split(":").map((part) => part.trim()).filter(Boolean).filter((part) => (0, import_path7.isAbsolute)(part));
trusted.push(...custom);
return trusted;
}
function isTrustedPrefix(resolvedPath) {
const normalized = (0, import_path7.normalize)(resolvedPath);
return getTrustedPrefixes().some((prefix) => normalized.startsWith((0, import_path7.normalize)(prefix)));
}
function assertBinaryName(binary) {
if (!/^[A-Za-z0-9._-]+$/.test(binary)) {
throw new Error(`Invalid CLI binary name: ${binary}`);
}
}
function resolveCliBinaryPath(binary) {
assertBinaryName(binary);
const cached = resolvedPathCache.get(binary);
if (cached) return cached;
const finder = process.platform === "win32" ? "where" : "which";
const result = (0, import_child_process2.spawnSync)(finder, [binary], {
timeout: 5e3,
env: process.env
});
if (result.status !== 0) {
throw new Error(`CLI binary '${binary}' not found in PATH`);
}
const stdout = result.stdout?.toString().trim() ?? "";
const firstLine = stdout.split("\n").map((line) => line.trim()).find(Boolean) ?? "";
if (!firstLine) {
throw new Error(`CLI binary '${binary}' not found in PATH`);
}
const resolvedPath = (0, import_path7.normalize)(firstLine);
if (!(0, import_path7.isAbsolute)(resolvedPath)) {
throw new Error(`Resolved CLI binary '${binary}' to relative path`);
}
if (UNTRUSTED_PATH_PATTERNS.some((pattern) => pattern.test(resolvedPath))) {
throw new Error(`Resolved CLI binary '${binary}' to untrusted location: ${resolvedPath}`);
}
if (!isTrustedPrefix(resolvedPath)) {
console.warn(`[omc:cli-security] CLI binary '${binary}' resolved to non-standard path: ${resolvedPath}`);
}
resolvedPathCache.set(binary, resolvedPath);
return resolvedPath;
}
var CONTRACTS = {
claude: {
agentType: "claude",
binary: "claude",
installInstructions: "Install Claude CLI: https://claude.ai/download",
buildLaunchArgs(model, extraFlags = []) {
const args = ["--dangerously-skip-permissions"];
if (model) {
const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
args.push("--model", resolved);
}
return [...args, ...extraFlags];
},
parseOutput(rawOutput) {
return rawOutput.trim();
}
},
codex: {
agentType: "codex",
binary: "codex",
installInstructions: "Install Codex CLI: npm install -g @openai/codex",
supportsPromptMode: true,
// Codex accepts prompt as a positional argument (no flag needed):
// codex [OPTIONS] [PROMPT]
buildLaunchArgs(model, extraFlags = []) {
const args = ["--dangerously-bypass-approvals-and-sandbox"];
if (model) args.push("--model", model);
return [...args, ...extraFlags];
},
parseOutput(rawOutput) {
const lines = rawOutput.trim().split("\n").filter(Boolean);
for (let i = lines.length - 1; i >= 0; i--) {
try {
const parsed = JSON.parse(lines[i]);
if (parsed.type === "message" && parsed.role === "assistant") {
return parsed.content ?? rawOutput;
}
if (parsed.type === "result" || parsed.output) {
return parsed.output ?? parsed.result ?? rawOutput;
}
} catch {
}
}
return rawOutput.trim();
}
},
gemini: {
agentType: "gemini",
binary: "gemini",
installInstructions: "Install Gemini CLI: npm install -g @google/gemini-cli",
supportsPromptMode: true,
promptModeFlag: "-i",
buildLaunchArgs(model, extraFlags = []) {
const args = ["--approval-mode", "yolo"];
if (model) args.push("--model", model);
return [...args, ...extraFlags];
},
parseOutput(rawOutput) {
return rawOutput.trim();
}
},
cursor: {
agentType: "cursor",
binary: "cursor-agent",
installInstructions: "Install Cursor Agent CLI: see https://docs.cursor.com/cli",
// cursor-agent runs as an interactive REPL — no exit-on-complete prompt mode.
// Keep supportsPromptMode false so the verdict-file contract path
// (CONTRACT_ROLES + shouldInjectContract) skips this provider; cursor
// workers participate as executors only.
supportsPromptMode: false,
buildLaunchArgs(_model, extraFlags = []) {
return [...extraFlags];
},
parseOutput(rawOutput) {
return rawOutput.trim();
}
}
};
function getContract(agentType) {
const contract = CONTRACTS[agentType];
if (!contract) {
throw new Error(`Unknown agent type: ${agentType}. Supported: ${Object.keys(CONTRACTS).join(", ")}`);
}
if (agentType !== "claude" && isExternalLLMDisabled()) {
throw new Error(
`External LLM provider "${agentType}" is blocked by security policy (disableExternalLLM). Only Claude workers are allowed in the current security configuration.`
);
}
return contract;
}
function validateBinaryRef(binary) {
if ((0, import_path7.isAbsolute)(binary)) return;
if (/^[A-Za-z0-9._-]+$/.test(binary)) return;
throw new Error(`Unsafe CLI binary reference: ${binary}`);
}
function resolveBinaryPath(binary) {
validateBinaryRef(binary);
if ((0, import_path7.isAbsolute)(binary)) return binary;
try {
const resolver = process.platform === "win32" ? "where" : "which";
const result = (0, import_child_process2.spawnSync)(resolver, [binary], { timeout: 5e3, encoding: "utf8" });
if (result.status !== 0) return binary;
const lines = result.stdout?.split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
const firstPath = lines[0];
const isResolvedAbsolute = !!firstPath && ((0, import_path7.isAbsolute)(firstPath) || import_path7.win32.isAbsolute(firstPath));
return isResolvedAbsolute ? firstPath : binary;
} catch {
return binary;
}
}
function resolveValidatedBinaryPath(agentType) {
const contract = getContract(agentType);
return resolveCliBinaryPath(contract.binary);
}
function buildLaunchArgs(agentType, config) {
return getContract(agentType).buildLaunchArgs(config.model, config.extraFlags);
}
function buildWorkerArgv(agentType, config) {
validateTeamName(config.teamName);
const contract = getContract(agentType);
const binary = config.resolvedBinaryPath ? (() => {
validateBinaryRef(config.resolvedBinaryPath);
return config.resolvedBinaryPath;
})() : resolveBinaryPath(contract.binary);
const args = buildLaunchArgs(agentType, config);
return [binary, ...args];
}
var WORKER_MODEL_ENV_ALLOWLIST = [
"ANTHROPIC_MODEL",
"CLAUDE_MODEL",
"ANTHROPIC_BASE_URL",
"CLAUDE_CODE_USE_BEDROCK",
"CLAUDE_CODE_USE_VERTEX",
"CLAUDE_CODE_BEDROCK_OPUS_MODEL",
"CLAUDE_CODE_BEDROCK_SONNET_MODEL",
"CLAUDE_CODE_BEDROCK_HAIKU_MODEL",
"ANTHROPIC_DEFAULT_OPUS_MODEL",
"ANTHROPIC_DEFAULT_SONNET_MODEL",
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
"OMC_MODEL_HIGH",
"OMC_MODEL_MEDIUM",
"OMC_MODEL_LOW",
"OMC_EXTERNAL_MODELS_DEFAULT_CODEX_MODEL",
"OMC_CODEX_DEFAULT_MODEL",
"OMC_EXTERNAL_MODELS_DEFAULT_GEMINI_MODEL",
"OMC_GEMINI_DEFAULT_MODEL"
];
function getWorkerEnv(teamName, workerName2, agentType, env = process.env) {
validateTeamName(teamName);
const workerEnv = {
OMC_TEAM_WORKER: `${teamName}/${workerName2}`,
OMC_TEAM_NAME: teamName,
OMC_WORKER_AGENT_TYPE: agentType
};
for (const key of WORKER_MODEL_ENV_ALLOWLIST) {
const value = env[key];
if (typeof value === "string" && value.length > 0) {
workerEnv[key] = value;
}
}
return workerEnv;
}
function isPromptModeAgent(agentType) {
const contract = getContract(agentType);
return !!contract.supportsPromptMode;
}
function resolveClaudeWorkerModel(env = process.env) {
if (env.OMC_ROUTING_FORCE_INHERIT === "true") {
return void 0;
}
if (!isBedrock() && !isVertexAI()) {
return void 0;
}
const directModel = env.ANTHROPIC_MODEL || env.CLAUDE_MODEL || "";
if (directModel) {
return directModel;
}
const bedrockModel = env.CLAUDE_CODE_BEDROCK_SONNET_MODEL || env.ANTHROPIC_DEFAULT_SONNET_MODEL || "";
if (bedrockModel) {
return bedrockModel;
}
const omcModel = env.OMC_MODEL_MEDIUM || "";
if (omcModel) {
return omcModel;
}
return void 0;
}
function getPromptModeArgs(agentType, instruction) {
const contract = getContract(agentType);
if (!contract.supportsPromptMode) {
return [];
}
if (contract.promptModeFlag) {
return [contract.promptModeFlag, instruction];
}
return [instruction];
}
// src/team/runtime.ts
init_team_name();
init_tmux_session();
// src/team/worker-bootstrap.ts
var import_promises2 = require("fs/promises");
var import_path10 = require("path");
// src/agents/prompt-helpers.ts
var import_fs6 = require("fs");
var import_path9 = require("path");
var import_url2 = require("url");
var import_meta2 = {};
function getPackageDir2() {
if (typeof __dirname !== "undefined" && __dirname) {
const currentDirName = (0, import_path9.basename)(__dirname);
const parentDirName = (0, import_path9.basename)((0, import_path9.dirname)(__dirname));
if (currentDirName === "bridge") {
return (0, import_path9.join)(__dirname, "..");
}
if (currentDirName === "agents" && (parentDirName === "src" || parentDirName === "dist")) {
return (0, import_path9.join)(__dirname, "..", "..");
}
}
try {
const __filename = (0, import_url2.fileURLToPath)(import_meta2.url);
const __dirname2 = (0, import_path9.dirname)(__filename);
const currentDirName = (0, import_path9.basename)(__dirname2);
if (currentDirName === "bridge") {
return (0, import_path9.join)(__dirname2, "..");
}
return (0, import_path9.join)(__dirname2, "..", "..");
} catch {
}
return process.cwd();
}
var _cachedRoles = null;
function getValidAgentRoles() {
if (_cachedRoles) return _cachedRoles;
try {
if (typeof __AGENT_ROLES__ !== "undefined" && Array.isArray(__AGENT_ROLES__) && __AGENT_ROLES__.length > 0) {
_cachedRoles = __AGENT_ROLES__;
return _cachedRoles;
}
} catch {
}
try {
const agentsDir = (0, import_path9.join)(getPackageDir2(), "agents");
const files = (0, import_fs6.readdirSync)(agentsDir);
_cachedRoles = files.filter((f) => f.endsWith(".md")).map((f) => (0, import_path9.basename)(f, ".md")).sort();
} catch (err) {
console.error("[prompt-injection] CRITICAL: Could not scan agents/ directory for role discovery:", err);
_cachedRoles = [];
}
return _cachedRoles;
}
var VALID_AGENT_ROLES = getValidAgentRoles();
function sanitizePromptContent(content, maxLength = 4e3) {
if (!content) return "";
let sanitized = content.length > maxLength ? content.slice(0, maxLength) : content;
if (sanitized.length > 0) {
const lastCode = sanitized.charCodeAt(sanitized.length - 1);
if (lastCode >= 55296 && lastCode <= 56319) {
sanitized = sanitized.slice(0, -1);
}
}
sanitized = sanitized.replace(/<(\/?)(system-instructions|system-reminder|TASK_SUBJECT|TASK_DESCRIPTION|INBOX_MESSAGE)(?=[\s>/])[^>]*>/gi, "[$1$2]");
return sanitized;
}
// src/utils/omc-cli-rendering.ts
var import_child_process3 = require("child_process");
var OMC_CLI_BINARY = "omc";
var OMC_PLUGIN_BRIDGE_PREFIX = 'node "$CLAUDE_PLUGIN_ROOT"/bridge/cli.cjs';
function commandExists(command, env) {
const lookupCommand = process.platform === "win32" ? "where" : "which";
const result = (0, import_child_process3.spawnSync)(lookupCommand, [command], {
stdio: "ignore",
env
});
return result.status === 0;
}
function isClaudeSession(env) {
return Boolean(
env.CLAUDECODE?.trim() || env.CLAUDE_SESSION_ID?.trim() || env.CLAUDECODE_SESSION_ID?.trim()
);
}
function resolveOmcCliPrefix(options = {}) {
const env = options.env ?? process.env;
const omcAvailable = options.omcAvailable ?? commandExists(OMC_CLI_BINARY, env);
if (omcAvailable) {
return OMC_CLI_BINARY;
}
const pluginRoot = typeof env.CLAUDE_PLUGIN_ROOT === "string" ? env.CLAUDE_PLUGIN_ROOT.trim() : "";
if (pluginRoot) {
return OMC_PLUGIN_BRIDGE_PREFIX;
}
return OMC_CLI_BINARY;
}
function resolveInvocationPrefix(commandSuffix, options = {}) {
const env = options.env ?? process.env;
const normalizedSuffix = commandSuffix.trim();
if (/^ask(?:\s|$)/.test(normalizedSuffix) && isClaudeSession(env)) {
return OMC_CLI_BINARY;
}
return resolveOmcCliPrefix(options);
}
function formatOmcCliInvocation(commandSuffix, options = {}) {
const suffix = commandSuffix.trim().replace(/^omc\s+/, "");
return `${resolveInvocationPrefix(suffix, options)} ${suffix}`.trim();
}
// src/team/worker-bootstrap.ts
function buildInstructionPath(...parts) {
return (0, import_path10.join)(...parts).replaceAll("\\", "/");
}
function generateTriggerMessage(teamName, workerName2, teamStateRoot2 = ".omc/state") {
const inboxPath = buildInstructionPath(teamStateRoot2, "team", teamName, "workers", workerName2, "inbox.md");
if (teamStateRoot2 !== ".omc/state") {
return `Read ${inboxPath}, work now, report progress.`;
}
return `Read ${inboxPath}, execute now, report concrete progress.`;
}
function generatePromptModeStartupPrompt(teamName, workerName2, teamStateRoot2 = ".omc/state", cliOutputContract) {
const inboxPath = buildInstructionPath(teamStateRoot2, "team", teamName, "workers", workerName2, "inbox.md");
const base = `Open ${inboxPath}. Follow it and begin the assigned work.`;
return cliOutputContract ? `${base}
${cliOutputContract}` : base;
}
function agentTypeGuidance(agentType) {
const teamApiCommand = formatOmcCliInvocation("team api");
const claimTaskCommand = formatOmcCliInvocation("team api claim-task");
const transitionTaskStatusCommand = formatOmcCliInvocation("team api transition-task-status");
switch (agentType) {
case "codex":
return [
"### Agent-Type Guidance (codex)",
`- Prefer short, explicit \`${teamApiCommand} ... --json\` commands and parse outputs before next step.`,
"- If a command fails, report the exact stderr to leader-fixed before retrying.",
`- You MUST run \`${claimTaskCommand}\` before starting work and \`${transitionTaskStatusCommand}\` when done.`
].join("\n");
case "gemini":
return [
"### Agent-Type Guidance (gemini)",
"- Execute task work in small, verifiable increments and report each milestone to leader-fixed.",
"- Keep commit-sized changes scoped to assigned files only; no broad refactors.",
`- CRITICAL: You MUST run \`${claimTaskCommand}\` before starting work and \`${transitionTaskStatusCommand}\` when done. Do not exit without transitioning the task status.`
].join("\n");
case "cursor":
return [
"### Agent-Type Guidance (cursor)",
"- You are an interactive REPL (cursor-agent), not a one-shot CLI. Stay in the session; the leader will continue to send prompts via mailbox.",
`- You MUST run \`${claimTaskCommand}\` before starting work and \`${transitionTaskStatusCommand}\` when done. Then keep waiting for the next mailbox message; do NOT type \`/exit\` unless the leader sends an explicit shutdown.`,
"- Reviewer/critic/security-review roles are NOT supported for cursor workers \u2014 those require a verdict-file write-and-exit which the REPL does not perform. Take only executor-style tasks."
].join("\n");
case "claude":
default:
return [
"### Agent-Type Guidance (claude)",
"- Keep reasoning focused on assigned task IDs and send concise progress acks to leader-fixed.",
"- Before any risky command, send a blocker/proposal message to leader-fixed and wait for updated inbox instructions."
].join("\n");
}
}
function generateWorkerOverlay(params) {
const { teamName, workerName: workerName2, agentType, tasks, bootstrapInstructions } = params;
const sanitizedTasks = tasks.map((t) => ({
id: t.id,
subject: sanitizePromptContent(t.subject),
description: sanitizePromptContent(t.description)
}));
const sentinelPath = `.omc/state/team/${teamName}/workers/${workerName2}/.ready`;
const heartbeatPath = `.omc/state/team/${teamName}/workers/${workerName2}/heartbeat.json`;
const inboxPath = `.omc/state/team/${teamName}/workers/${workerName2}/inbox.md`;
const statusPath = `.omc/state/team/${teamName}/workers/${workerName2}/status.json`;
const claimTaskCommand = formatOmcCliInvocation(`team api claim-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"worker\\":\\"${workerName2}\\"}" --json`);
const sendAckCommand = formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName2}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"ACK: ${workerName2} initialized\\"}" --json`);
const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
const failTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"failed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
const readTaskCommand = formatOmcCliInvocation(`team api read-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\"}" --json`);
const releaseClaimCommand = formatOmcCliInvocation(`team api release-task-claim --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"claim_token\\":\\"<claim_token>\\",\\"worker\\":\\"${workerName2}\\"}" --json`);
const mailboxListCommand = formatOmcCliInvocation(`team api mailbox-list --input "{\\"team_name\\":\\"${teamName}\\",\\"worker\\":\\"${workerName2}\\"}" --json`);
const mailboxDeliveredCommand = formatOmcCliInvocation(`team api mailbox-mark-delivered --input "{\\"team_name\\":\\"${teamName}\\",\\"worker\\":\\"${workerName2}\\",\\"message_id\\":\\"<id>\\"}" --json`);
const teamApiCommand = formatOmcCliInvocation("team api");
const teamCommand = formatOmcCliInvocation("team");
const taskList = sanitizedTasks.length > 0 ? sanitizedTasks.map((t) => `- **Task ${t.id}**: ${t.subject}
Description: ${t.description}
Status: pending`).join("\n") : "- No tasks assigned yet. Check your inbox for assignments.";
return `# Team Worker Protocol
You are a **team worker**, not the team leader. Operate strictly within worker protocol.
## FIRST ACTION REQUIRED
Before doing anything else, write your ready sentinel file:
\`\`\`bash
mkdir -p $(dirname ${sentinelPath}) && touch ${sentinelPath}
\`\`\`
## MANDATORY WORKFLOW \u2014 Follow These Steps In Order
You MUST complete ALL of these steps. Do NOT skip any step. Do NOT exit without step 4.
1. **Claim** your task (run this command first):
\`${claimTaskCommand}\`
Save the \`claim_token\` from the response \u2014 you need it for step 4.
2. **Do the work** described in your task assignment below.
3. **Send ACK** to the leader:
\`${sendAckCommand}\`
4. **Transition** the task status (REQUIRED before exit):
- On success: \`${completeTaskCommand}\`
- On failure: \`${failTaskCommand}\`
5. **Keep going after replies**: ACK/progress messages are not a stop signal. Keep executing your assigned or next feasible work until the task is actually complete or failed, then transition and exit.
## Identity
- **Team**: ${teamName}
- **Worker**: ${workerName2}
- **Agent Type**: ${agentType}
- **Environment**: OMC_TEAM_WORKER=${teamName}/${workerName2}
## Your Tasks
${taskList}
## Task Lifecycle Reference (CLI API)
Use the CLI API for all task lifecycle operations. Do NOT directly edit task files.
- Inspect task state: \`${readTaskCommand}\`
- Task id format: State/CLI APIs use task_id: "<id>" (example: "1"), not "task-1"
- Claim task: \`${claimTaskCommand}\`
- Complete task: \`${completeTaskCommand}\`
- Fail task: \`${failTaskCommand}\`
- Release claim (rollback): \`${releaseClaimCommand}\`
## Communication Protocol
- **Inbox**: Read ${inboxPath} for new instructions
- **Status**: Write to ${statusPath}:
\`\`\`json
{"state": "idle", "updated_at": "<ISO timestamp>"}
\`\`\`
States: "idle" | "working" | "blocked" | "done" | "failed"
- **Heartbeat**: Update ${heartbeatPath} every few minutes:
\`\`\`json
{"pid":<pid>,"last_turn_at":"<ISO timestamp>","turn_count":<n>,"alive":true}
\`\`\`
## Message Protocol
Send messages via CLI API:
- To leader: \`${formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName2}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"<message>\\"}" --json`)}\`
- Check mailbox: \`${mailboxListCommand}\`
- Mark delivered: \`${mailboxDeliveredCommand}\`
## Startup Handshake (Required)
Before doing any task work, send exactly one startup ACK to the leader:
\`${sendAckCommand}\`
## Shutdown Protocol
When you see a shutdown request in your inbox:
1. Write your decision to: .omc/state/team/${teamName}/workers/${workerName2}/shutdown-ack.json
2. Format:
- Accept: {"status":"accept","reason":"ok","updated_at":"<iso>"}
- Reject: {"status":"reject","reason":"still working","updated_at":"<iso>"}
3. Exit your session
## Rules
- You are NOT the leader. Never run leader orchestration workflows.
- Do NOT edit files outside the paths listed in your task description
- Do NOT write lifecycle fields (status, owner, result, error) directly in task files; use CLI API
- Do NOT spawn sub-agents. Complete work in this worker session only.
- Do NOT create tmux panes/sessions (\`tmux split-window\`, \`tmux new-session\`, etc.).
- Do NOT run team spawning/orchestration commands (for example: \`${teamCommand} ...\`, \`omx team ...\`, \`$team\`, \`$ultrawork\`, \`$autopilot\`, \`$ralph\`).
- Worker-allowed control surface is only: \`${teamApiCommand} ... --json\` (and equivalent \`omx team api ... --json\` where configured).
- If blocked, write {"state": "blocked", "reason": "..."} to your status file
${agentTypeGuidance(agentType)}
## BEFORE YOU EXIT
You MUST call \`${formatOmcCliInvocation("team api transition-task-status")}\` to mark your task as "completed" or "failed" before exiting.
If you skip this step, the leader cannot track your work and the task will appear stuck.
${bootstrapInstructions ? `## Role Context
${bootstrapInstructions}
` : ""}`;
}
async function composeInitialInbox(teamName, workerName2, content, cwd, cliOutputContract) {
const inboxPath = (0, import_path10.join)(cwd, `.omc/state/team/${teamName}/workers/${workerName2}/inbox.md`);
await (0, import_promises2.mkdir)((0, import_path10.dirname)(inboxPath), { recursive: true });
const finalContent = cliOutputContract && !content.includes(cliOutputContract) ? `${content}
${cliOutputContract}` : content;
await (0, import_promises2.writeFile)(inboxPath, finalContent, "utf-8");
}
async function ensureWorkerStateDir(teamName, workerName2, cwd) {
const workerDir = (0, import_path10.join)(cwd, `.omc/state/team/${teamName}/workers/${workerName2}`);
await (0, import_promises2.mkdir)(workerDir, { recursive: true });
const mailboxDir = (0, import_path10.join)(cwd, `.omc/state/team/${teamName}/mailbox`);
await (0, import_promises2.mkdir)(mailboxDir, { recursive: true });
const tasksDir = (0, import_path10.join)(cwd, `.omc/state/team/${teamName}/tasks`);
await (0, import_promises2.mkdir)(tasksDir, { recursive: true });
}
async function writeWorkerOverlay(params) {
const { teamName, workerName: workerName2, cwd } = params;
const overlay = generateWorkerOverlay(params);
const overlayPath = (0, import_path10.join)(cwd, `.omc/state/team/${teamName}/workers/${workerName2}/AGENTS.md`);
await (0, import_promises2.mkdir)((0, import_path10.dirname)(overlayPath), { recursive: true });
await (0, import_promises2.writeFile)(overlayPath, overlay, "utf-8");
return overlayPath;
}
// src/team/git-worktree.ts
var import_node_fs = require("node:fs");
var import_node_path = require("node:path");
var import_node_child_process = require("node:child_process");
// src/team/fs-utils.ts
var import_fs7 = require("fs");
var import_path11 = require("path");
function atomicWriteJson(filePath, data, mode = 384) {
const dir = (0, import_path11.dirname)(filePath);
if (!(0, import_fs7.existsSync)(dir)) (0, import_fs7.mkdirSync)(dir, { recursive: true, mode: 448 });
const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
(0, import_fs7.writeFileSync)(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode });
(0, import_fs7.renameSync)(tmpPath, filePath);
}
function ensureDirWithMode(dirPath, mode = 448) {
if (!(0, import_fs7.existsSync)(dirPath)) (0, import_fs7.mkdirSync)(dirPath, { recursive: true, mode });
}
function safeRealpath(p) {
try {
return (0, import_fs7.realpathSync)(p);
} catch {
const segments = [];
let current = (0, import_path11.resolve)(p);
while (!(0, import_fs7.existsSync)(current)) {
segments.unshift((0, import_path11.basename)(current));
const parent = (0, import_path11.dirname)(current);
if (parent === current) break;
current = parent;
}
try {
return (0, import_path11.join)((0, import_fs7.realpathSync)(current), ...segments);
} catch {
return (0, import_path11.resolve)(p);
}
}
}
function validateResolvedPath(resolvedPath, expectedBase) {
const absResolved = safeRealpath(resolvedPath);
const absBase = safeRealpath(expectedBase);
const rel = (0, import_path11.relative)(absBase, absResolved);
if (rel.startsWith("..") || (0, import_path11.resolve)(absBase, rel) !== absResolved) {
throw new Error(`Path traversal detected: "${resolvedPath}" escapes base "${expectedBase}"`);
}
}
// src/team/git-worktree.ts
init_tmux_session();
init_file_lock();
function getWorktreePath(repoRoot, teamName, workerName2) {
return (0, import_node_path.join)(repoRoot, ".omc", "worktrees", sanitizeName(teamName), sanitizeName(workerName2));
}
function getBranchName(teamName, workerName2) {
return `omc-team/${sanitizeName(teamName)}/${sanitizeName(workerName2)}`;
}
function getMetadataPath(repoRoot, teamName) {
return (0, import_node_path.join)(repoRoot, ".omc", "state", "team-bridge", sanitizeName(teamName), "worktrees.json");
}
function readMetadata(repoRoot, teamName) {
const metaPath = getMetadataPath(repoRoot, teamName);
if (!(0, import_node_fs.existsSync)(metaPath)) return [];
try {
return JSON.parse((0, import_node_fs.readFileSync)(metaPath, "utf-8"));
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
process.stderr.write(`[omc] warning: worktrees.json parse error: ${msg}
`);
return [];
}
}
function writeMetadata(repoRoot, teamName, entries) {
const metaPath = getMetadataPath(repoRoot, teamName);
validateResolvedPath(metaPath, repoRoot);
const dir = (0, import_node_path.join)(repoRoot, ".omc", "state", "team-bridge", sanitizeName(teamName));
ensureDirWithMode(dir);
atomicWriteJson(metaPath, entries);
}
function removeWorkerWorktree(teamName, workerName2, repoRoot) {
const wtPath = getWorktreePath(repoRoot, teamName, workerName2);
const branch = getBranchName(teamName, workerName2);
try {
(0, import_node_child_process.execFileSync)("git", ["worktree", "remove", "--force", wtPath], { cwd: repoRoot, stdio: "pipe" });
} catch {
}
try {
(0, import_node_child_process.execFileSync)("git", ["worktree", "prune"], { cwd: repoRoot, stdio: "pipe" });
} catch {
}
try {
(0, import_node_child_process.execFileSync)("git", ["branch", "-D", branch], { cwd: repoRoot, stdio: "pipe" });
} catch {
}
const metaLockPath = getMetadataPath(repoRoot, teamName) + ".lock";
withFileLockSync(metaLockPath, () => {
const existing = readMetadata(repoRoot, teamName);
const updated = existing.filter((e) => e.workerName !== workerName2);
writeMetadata(repoRoot, teamName, updated);
});
}
function cleanupTeamWorktrees(teamName, repoRoot) {
const entries = readMetadata(repoRoot, teamName);
for (const entry of entries) {
try {
removeWorkerWorktree(teamName, entry.workerName, repoRoot);
} catch {
}
}
}
// src/team/task-file-ops.ts
var import_fs10 = require("fs");
var import_path13 = require("path");
init_tmux_session();
init_platform();
// src/team/state-paths.ts
var import_path12 = require("path");
function normalizeTaskFileStem(taskId) {
const trimmed = String(taskId).trim().replace(/\.json$/i, "");
if (/^task-\d+$/.test(trimmed)) return trimmed;
if (/^\d+$/.test(trimmed)) return `task-${trimmed}`;
return trimmed;
}
var TeamPaths = {
root: (teamName) => `.omc/state/team/${teamName}`,
config: (teamName) => `.omc/state/team/${teamName}/config.json`,
shutdown: (teamName) => `.omc/state/team/${teamName}/shutdown.json`,
tasks: (teamName) => `.omc/state/team/${teamName}/tasks`,
taskFile: (teamName, taskId) => `.omc/state/team/${teamName}/tasks/${normalizeTaskFileStem(taskId)}.json`,
workers: (teamName) => `.omc/state/team/${teamName}/workers`,
workerDir: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}`,
heartbeat: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/heartbeat.json`,
inbox: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/inbox.md`,
outbox: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/outbox.jsonl`,
ready: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/.ready`,
overlay: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/AGENTS.md`,
shutdownAck: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/shutdown-ack.json`,
mailbox: (teamName, workerName2) => `.omc/state/team/${teamName}/mailbox/${workerName2}.json`,
mailboxLockDir: (teamName, workerName2) => `.omc/state/team/${teamName}/mailbox/.lock-${workerName2}`,
dispatchRequests: (teamName) => `.omc/state/team/${teamName}/dispatch/requests.json`,
dispatchLockDir: (teamName) => `.omc/state/team/${teamName}/dispatch/.lock`,
workerStatus: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/status.json`,
workerIdleNotify: (teamName) => `.omc/state/team/${teamName}/worker-idle-notify.json`,
workerPrevNotifyState: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/prev-notify-state.json`,
events: (teamName) => `.omc/state/team/${teamName}/events.jsonl`,
approval: (teamName, taskId) => `.omc/state/team/${teamName}/approvals/${taskId}.json`,
manifest: (teamName) => `.omc/state/team/${teamName}/manifest.json`,
monitorSnapshot: (teamName) => `.omc/state/team/${teamName}/monitor-snapshot.json`,
summarySnapshot: (teamName) => `.omc/state/team/${teamName}/summary-snapshot.json`,
phaseState: (teamName) => `.omc/state/team/${teamName}/phase-state.json`,
scalingLock: (teamName) => `.omc/state/team/${teamName}/.scaling-lock`,
workerIdentity: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/identity.json`,
workerAgentsMd: (teamName) => `.omc/state/team/${teamName}/worker-agents.md`,
shutdownRequest: (teamName, workerName2) => `.omc/state/team/${teamName}/workers/${workerName2}/shutdown-request.json`
};
function absPath(cwd, relativePath) {
return (0, import_path12.isAbsolute)(relativePath) ? relativePath : (0, import_path12.join)(cwd, relativePath);
}
function teamStateRoot(cwd, teamName) {
return (0, import_path12.join)(cwd, TeamPaths.root(teamName));
}
function getTaskStoragePath(cwd, teamName, taskId) {
if (taskId !== void 0) {
return (0, import_path12.join)(cwd, TeamPaths.taskFile(teamName, taskId));
}
return (0, import_path12.join)(cwd, TeamPaths.tasks(teamName));
}
// src/team/task-file-ops.ts
var DEFAULT_STALE_LOCK_MS2 = 3e4;
function acquireTaskLock(teamName, taskId, opts) {
const staleLockMs = opts?.staleLockMs ?? DEFAULT_STALE_LOCK_MS2;
const dir = canonicalTasksDir(teamName, opts?.cwd);
ensureDirWithMode(dir);
const lockPath = (0, import_path13.join)(dir, `${sanitizeTaskId(taskId)}.lock`);
for (let attempt = 0; attempt < 2; attempt++) {
try {
const fd = (0, import_fs10.openSync)(lockPath, import_fs10.constants.O_CREAT | import_fs10.constants.O_EXCL | import_fs10.constants.O_WRONLY, 384);
const payload = JSON.stringify({
pid: process.pid,
workerName: opts?.workerName ?? "",
timestamp: Date.now()
});
(0, import_fs10.writeSync)(fd, payload, null, "utf-8");
return { fd, path: lockPath };
} catch (err) {
if (err && typeof err === "object" && "code" in err && err.code === "EEXIST") {
if (attempt === 0 && isLockStale2(lockPath, staleLockMs)) {
try {
(0, import_fs10.unlinkSync)(lockPath);
} catch {
}
continue;
}
return null;
}
throw err;
}
}
return null;
}
function releaseTaskLock(handle) {
try {
(0, import_fs10.closeSync)(handle.fd);
} catch {
}
try {
(0, import_fs10.unlinkSync)(handle.path);
} catch {
}
}
async function withTaskLock(teamName, taskId, fn, opts) {
const handle = acquireTaskLock(teamName, taskId, opts);
if (!handle) return null;
try {
return await fn();
} finally {
releaseTaskLock(handle);
}
}
function isLockStale2(lockPath, staleLockMs) {
try {
const stat2 = (0, import_fs10.statSync)(lockPath);
const ageMs = Date.now() - stat2.mtimeMs;
if (ageMs < staleLockMs) return false;
try {
const raw = (0, import_fs10.readFileSync)(lockPath, "utf-8");
const payload = JSON.parse(raw);
if (payload.pid && isProcessAlive(payload.pid)) return false;
} catch {
}
return true;
} catch {
return false;
}
}
function sanitizeTaskId(taskId) {
if (!/^[A-Za-z0-9._-]+$/.test(taskId)) {
throw new Error(`Invalid task ID: "${taskId}" contains unsafe characters`);
}
return taskId;
}
function canonicalTasksDir(teamName, cwd) {
const root = cwd ?? process.cwd();
const dir = getTaskStoragePath(root, sanitizeName(teamName));
validateResolvedPath(dir, (0, import_path13.join)(root, ".omc", "state", "team"));
return dir;
}
function failureSidecarPath(teamName, taskId, cwd) {
return (0, import_path13.join)(canonicalTasksDir(teamName, cwd), `${sanitizeTaskId(taskId)}.failure.json`);
}
function writeTaskFailure(teamName, taskId, error, opts) {
const filePath = failureSidecarPath(teamName, taskId, opts?.cwd);
const existing = readTaskFailure(teamName, taskId, opts);
const sidecar = {
taskId,
lastError: error,
retryCount: existing ? existing.retryCount + 1 : 1,
lastFailedAt: (/* @__PURE__ */ new Date()).toISOString()
};
atomicWriteJson(filePath, sidecar);
return sidecar;
}
function readTaskFailure(teamName, taskId, opts) {
const filePath = failureSidecarPath(teamName, taskId, opts?.cwd);
if (!(0, import_fs10.existsSync)(filePath)) return null;
try {
const raw = (0, import_fs10.readFileSync)(filePath, "utf-8");
return JSON.parse(raw);
} catch {
return null;
}
}
var DEFAULT_MAX_TASK_RETRIES = 5;
// src/team/runtime.ts
function workerName(index) {
return `worker-${index + 1}`;
}
function stateRoot(cwd, teamName) {
validateTeamName(teamName);
return (0, import_path14.join)(cwd, `.omc/state/team/${teamName}`);
}
async function writeJson(filePath, data) {
await (0, import_promises3.mkdir)((0, import_path14.join)(filePath, ".."), { recursive: true });
await (0, import_promises3.writeFile)(filePath, JSON.stringify(data, null, 2), "utf-8");
}
async function readJsonSafe(filePath) {
const isDoneSignalPath = filePath.endsWith("done.json");
const maxAttempts = isDoneSignalPath ? 4 : 1;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const content = await (0, import_promises3.readFile)(filePath, "utf-8");
try {
return JSON.parse(content);
} catch {
if (!isDoneSignalPath || attempt === maxAttempts) {
return null;
}
}
} catch (error) {
const isMissingDoneSignal = isDoneSignalPath && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
if (isMissingDoneSignal) {
return null;
}
if (!isDoneSignalPath || attempt === maxAttempts) {
return null;
}
}
await new Promise((resolve5) => setTimeout(resolve5, 25));
}
return null;
}
function parseWorkerIndex(workerNameValue) {
const match = workerNameValue.match(/^worker-(\d+)$/);
if (!match) return 0;
const parsed = Number.parseInt(match[1], 10) - 1;
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
}
function taskPath(root, taskId) {
return (0, import_path14.join)(root, "tasks", `${taskId}.json`);
}
async function writePanesTrackingFileIfPresent(runtime) {
const jobId = process.env.OMC_JOB_ID;
const omcJobsDir = process.env.OMC_JOBS_DIR;
if (!jobId || !omcJobsDir) return;
const panesPath = (0, import_path14.join)(omcJobsDir, `${jobId}-panes.json`);
const tempPath = `${panesPath}.tmp`;
await (0, import_promises3.writeFile)(
tempPath,
JSON.stringify({
paneIds: [...runtime.workerPaneIds],
leaderPaneId: runtime.leaderPaneId,
sessionName: runtime.sessionName,
ownsWindow: Boolean(runtime.ownsWindow)
}),
"utf-8"
);
await (0, import_promises3.rename)(tempPath, panesPath);
}
async function readTask(root, taskId) {
return readJsonSafe(taskPath(root, taskId));
}
async function writeTask(root, task) {
await writeJson(taskPath(root, task.id), task);
}
async function markTaskInProgress(root, taskId, owner, teamName, cwd) {
const result = await withTaskLock(teamName, taskId, async () => {
const task = await readTask(root, taskId);
if (!task || task.status !== "pending") return false;
task.status = "in_progress";
task.owner = owner;
task.assignedAt = (/* @__PURE__ */ new Date()).toISOString();
await writeTask(root, task);
return true;
}, { cwd });
return result ?? false;
}
async function resetTaskToPending(root, taskId, teamName, cwd) {
await withTaskLock(teamName, taskId, async () => {
const task = await readTask(root, taskId);
if (!task) return;
task.status = "pending";
task.owner = null;
task.assignedAt = void 0;
await writeTask(root, task);
}, { cwd });
}
async function markTaskFromDone(root, teamName, cwd, taskId, status, summary) {
await withTaskLock(teamName, taskId, async () => {
const task = await readTask(root, taskId);
if (!task) return;
task.status = status;
task.result = summary;
task.summary = summary;
if (status === "completed") {
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
} else {
task.failedAt = (/* @__PURE__ */ new Date()).toISOString();
}
await writeTask(root, task);
}, { cwd });
}
async function applyDeadPaneTransition(runtime, workerNameValue, taskId) {
const root = stateRoot(runtime.cwd, runtime.teamName);
const transition = await withTaskLock(runtime.teamName, taskId, async () => {
const task = await readTask(root, taskId);
if (!task) return { action: "skipped" };
if (task.status === "completed" || task.status === "failed") {
return { action: "skipped" };
}
if (task.status !== "in_progress" || task.owner !== workerNameValue) {
return { action: "skipped" };
}
const failure = await writeTaskFailure(
runtime.teamName,
taskId,
`Worker pane died before done.json was written (${workerNameValue})`,
{ cwd: runtime.cwd }
);
const retryCount = failure.retryCount;
if (retryCount >= DEFAULT_MAX_TASK_RETRIES) {
task.status = "failed";
task.owner = workerNameValue;
task.summary = `Worker pane died before done.json was written (${workerNameValue})`;
task.result = task.summary;
task.failedAt = (/* @__PURE__ */ new Date()).toISOString();
await writeTask(root, task);
return { action: "failed", retryCount };
}
task.status = "pending";
task.owner = null;
task.assignedAt = void 0;
await writeTask(root, task);
return { action: "requeued", retryCount };
}, { cwd: runtime.cwd });
return transition ?? { action: "skipped" };
}
async function nextPendingTaskIndex(runtime) {
const root = stateRoot(runtime.cwd, runtime.teamName);
const transientReadRetryAttempts = 3;
const transientReadRetryDelayMs = 15;
for (let i = 0; i < runtime.config.tasks.length; i++) {
const taskId = String(i + 1);
let task = await readTask(root, taskId);
if (!task) {
for (let attempt = 1; attempt < transientReadRetryAttempts; attempt++) {
await new Promise((resolve5) => setTimeout(resolve5, transientReadRetryDelayMs));
task = await readTask(root, taskId);
if (task) break;
}
}
if (task?.status === "pending") return i;
}
return null;
}
async function notifyPaneWithRetry(sessionName2, paneId, message, maxAttempts = 6, retryDelayMs = 350) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
if (await sendToWorker(sessionName2, paneId, message)) {
return true;
}
if (attempt < maxAttempts) {
await new Promise((r) => setTimeout(r, retryDelayMs));
}
}
return false;
}
async function allTasksTerminal(runtime) {
const root = stateRoot(runtime.cwd, runtime.teamName);
for (let i = 0; i < runtime.config.tasks.length; i++) {
const task = await readTask(root, String(i + 1));
if (!task) return false;
if (task.status !== "completed" && task.status !== "failed") return false;
}
return true;
}
function buildInitialTaskInstruction(teamName, workerName2, task, taskId) {
const donePath = `.omc/state/team/${teamName}/workers/${workerName2}/done.json`;
return [
`## Initial Task Assignment`,
`Task ID: ${taskId}`,
`Worker: ${workerName2}`,
`Subject: ${task.subject}`,
``,
task.description,
``,
`When complete, write done signal to ${donePath}:`,
`{"taskId":"${taskId}","status":"completed","summary":"<brief summary>","completedAt":"<ISO timestamp>"}`,
``,
`IMPORTANT: Execute ONLY the task assigned to you in this inbox. After writing done.json, exit immediately. Do not read from the task directory or claim other tasks.`
].join("\n");
}
async function startTeam(config) {
const { teamName, agentTypes, tasks, cwd } = config;
validateTeamName(teamName);
const resolvedBinaryPaths = {};
for (const agentType of [...new Set(agentTypes)]) {
resolvedBinaryPaths[agentType] = resolveValidatedBinaryPath(agentType);
}
const root = stateRoot(cwd, teamName);
await (0, import_promises3.mkdir)((0, import_path14.join)(root, "tasks"), { recursive: true });
await (0, import_promises3.mkdir)((0, import_path14.join)(root, "mailbox"), { recursive: true });
await writeJson((0, import_path14.join)(root, "config.json"), config);
for (let i = 0; i < tasks.length; i++) {
const taskId = String(i + 1);
await writeJson((0, import_path14.join)(root, "tasks", `${taskId}.json`), {
id: taskId,
subject: tasks[i].subject,
description: tasks[i].description,
status: "pending",
owner: null,
result: null,
createdAt: (/* @__PURE__ */ new Date()).toISOString()
});
}
const workerNames = [];
for (let i = 0; i < tasks.length; i++) {
const wName = workerName(i);
workerNames.push(wName);
const agentType = agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude";
await ensureWorkerStateDir(teamName, wName, cwd);
await writeWorkerOverlay({
teamName,
workerName: wName,
agentType,
tasks: tasks.map((t, idx) => ({ id: String(idx + 1), subject: t.subject, description: t.description })),
cwd
});
}
const session = await createTeamSession(teamName, 0, cwd, {
newWindow: Boolean(config.newWindow)
});
const runtime = {
teamName,
sessionName: session.sessionName,
leaderPaneId: session.leaderPaneId,
config: {
...config,
tmuxSession: session.sessionName,
leaderPaneId: session.leaderPaneId,
tmuxOwnsWindow: session.sessionMode !== "split-pane"
},
workerNames,
workerPaneIds: session.workerPaneIds,
// initially empty []
activeWorkers: /* @__PURE__ */ new Map(),
cwd,
resolvedBinaryPaths,
ownsWindow: session.sessionMode !== "split-pane"
};
await writeJson((0, import_path14.join)(root, "config.json"), runtime.config);
const maxConcurrentWorkers = agentTypes.length;
for (let i = 0; i < maxConcurrentWorkers; i++) {
const taskIndex = await nextPendingTaskIndex(runtime);
if (taskIndex == null) break;
await spawnWorkerForTask(runtime, workerName(i), taskIndex);
}
runtime.stopWatchdog = watchdogCliWorkers(runtime, 1e3);
return runtime;
}
async function monitorTeam(teamName, cwd, workerPaneIds) {
validateTeamName(teamName);
const monitorStartedAt = Date.now();
const root = stateRoot(cwd, teamName);
const taskScanStartedAt = Date.now();
const taskCounts = { pending: 0, inProgress: 0, completed: 0, failed: 0 };
try {
const { readdir: readdir2 } = await import("fs/promises");
const taskFiles = await readdir2((0, import_path14.join)(root, "tasks"));
for (const f of taskFiles.filter((f2) => f2.endsWith(".json"))) {
const task = await readJsonSafe((0, import_path14.join)(root, "tasks", f));
if (task?.status === "pending") taskCounts.pending++;
else if (task?.status === "in_progress") taskCounts.inProgress++;
else if (task?.status === "completed") taskCounts.completed++;
else if (task?.status === "failed") taskCounts.failed++;
}
} catch {
}
const listTasksMs = Date.now() - taskScanStartedAt;
const workerScanStartedAt = Date.now();
const workers = [];
const deadWorkers = [];
for (let i = 0; i < workerPaneIds.length; i++) {
const wName = `worker-${i + 1}`;
const paneId = workerPaneIds[i];
const alive = await isWorkerAlive(paneId);
const heartbeatPath = (0, import_path14.join)(root, "workers", wName, "heartbeat.json");
const heartbeat = await readJsonSafe(heartbeatPath);
let stalled = false;
if (heartbeat?.updatedAt) {
const age = Date.now() - new Date(heartbeat.updatedAt).getTime();
stalled = age > 6e4;
}
const status = {
workerName: wName,
alive,
paneId,
currentTaskId: heartbeat?.currentTaskId,
lastHeartbeat: heartbeat?.updatedAt,
stalled
};
workers.push(status);
if (!alive) deadWorkers.push(wName);
}
const workerScanMs = Date.now() - workerScanStartedAt;
let phase = "executing";
if (taskCounts.inProgress === 0 && taskCounts.pending > 0 && taskCounts.completed === 0) {
phase = "planning";
} else if (taskCounts.failed > 0 && taskCounts.pending === 0 && taskCounts.inProgress === 0) {
phase = "fixing";
} else if (taskCounts.completed > 0 && taskCounts.pending === 0 && taskCounts.inProgress === 0 && taskCounts.failed === 0) {
phase = "completed";
}
return {
teamName,
phase,
workers,
taskCounts,
deadWorkers,
monitorPerformance: {
listTasksMs,
workerScanMs,
totalMs: Date.now() - monitorStartedAt
}
};
}
function watchdogCliWorkers(runtime, intervalMs) {
let tickInFlight = false;
let consecutiveFailures = 0;
const MAX_CONSECUTIVE_FAILURES = 3;
const unresponsiveCounts = /* @__PURE__ */ new Map();
const UNRESPONSIVE_KILL_THRESHOLD = 3;
const tick = async () => {
if (tickInFlight) return;
tickInFlight = true;
try {
const workers = [...runtime.activeWorkers.entries()];
if (workers.length === 0) return;
const root = stateRoot(runtime.cwd, runtime.teamName);
const [doneSignals, aliveResults] = await Promise.all([
Promise.all(workers.map(([wName]) => {
const donePath = (0, import_path14.join)(root, "workers", wName, "done.json");
return readJsonSafe(donePath);
})),
Promise.all(workers.map(([, active]) => isWorkerAlive(active.paneId)))
]);
for (let i = 0; i < workers.length; i++) {
const [wName, active] = workers[i];
const donePath = (0, import_path14.join)(root, "workers", wName, "done.json");
const signal = doneSignals[i];
if (signal) {
unresponsiveCounts.delete(wName);
await markTaskFromDone(root, runtime.teamName, runtime.cwd, signal.taskId || active.taskId, signal.status, signal.summary);
try {
const { unlink: unlink3 } = await import("fs/promises");
await unlink3(donePath);
} catch {
}
await killWorkerPane(runtime, wName, active.paneId);
if (!await allTasksTerminal(runtime)) {
const nextTaskIndexValue = await nextPendingTaskIndex(runtime);
if (nextTaskIndexValue != null) {
await spawnWorkerForTask(runtime, wName, nextTaskIndexValue);
}
}
continue;
}
const alive = aliveResults[i];
if (!alive) {
unresponsiveCounts.delete(wName);
const transition = await applyDeadPaneTransition(runtime, wName, active.taskId);
if (transition.action === "requeued") {
const retryCount = transition.retryCount ?? 1;
console.warn(`[watchdog] worker ${wName} dead pane \u2014 requeuing task ${active.taskId} (retry ${retryCount}/${DEFAULT_MAX_TASK_RETRIES})`);
}
await killWorkerPane(runtime, wName, active.paneId);
if (!await allTasksTerminal(runtime)) {
const nextTaskIndexValue = await nextPendingTaskIndex(runtime);
if (nextTaskIndexValue != null) {
await spawnWorkerForTask(runtime, wName, nextTaskIndexValue);
}
}
continue;
}
const heartbeatPath = (0, import_path14.join)(root, "workers", wName, "heartbeat.json");
const heartbeat = await readJsonSafe(heartbeatPath);
const isStalled = heartbeat?.updatedAt ? Date.now() - new Date(heartbeat.updatedAt).getTime() > 6e4 : false;
if (isStalled) {
const count = (unresponsiveCounts.get(wName) ?? 0) + 1;
unresponsiveCounts.set(wName, count);
if (count < UNRESPONSIVE_KILL_THRESHOLD) {
console.warn(`[watchdog] worker ${wName} unresponsive (${count}/${UNRESPONSIVE_KILL_THRESHOLD}), task ${active.taskId}`);
} else {
console.warn(`[watchdog] worker ${wName} unresponsive ${count} consecutive ticks \u2014 killing and reassigning task ${active.taskId}`);
unresponsiveCounts.delete(wName);
const transition = await applyDeadPaneTransition(runtime, wName, active.taskId);
if (transition.action === "requeued") {
console.warn(`[watchdog] worker ${wName} stall-killed \u2014 requeuing task ${active.taskId} (retry ${transition.retryCount}/${DEFAULT_MAX_TASK_RETRIES})`);
}
await killWorkerPane(runtime, wName, active.paneId);
if (!await allTasksTerminal(runtime)) {
const nextTaskIndexValue = await nextPendingTaskIndex(runtime);
if (nextTaskIndexValue != null) {
await spawnWorkerForTask(runtime, wName, nextTaskIndexValue);
}
}
}
} else {
unresponsiveCounts.delete(wName);
}
}
consecutiveFailures = 0;
} catch (err) {
consecutiveFailures++;
console.warn("[watchdog] tick error:", err);
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
console.warn(`[watchdog] ${consecutiveFailures} consecutive failures \u2014 marking team as failed`);
try {
const root = stateRoot(runtime.cwd, runtime.teamName);
await writeJson((0, import_path14.join)(root, "watchdog-failed.json"), {
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
consecutiveFailures,
lastError: err instanceof Error ? err.message : String(err)
});
} catch {
}
clearInterval(intervalId);
}
} finally {
tickInFlight = false;
}
};
const intervalId = setInterval(() => {
tick();
}, intervalMs);
return () => clearInterval(intervalId);
}
async function spawnWorkerForTask(runtime, workerNameValue, taskIndex) {
const root = stateRoot(runtime.cwd, runtime.teamName);
const taskId = String(taskIndex + 1);
const task = runtime.config.tasks[taskIndex];
if (!task) return "";
const marked = await markTaskInProgress(root, taskId, workerNameValue, runtime.teamName, runtime.cwd);
if (!marked) return "";
const splitTarget = runtime.workerPaneIds.length === 0 ? runtime.leaderPaneId : runtime.workerPaneIds[runtime.workerPaneIds.length - 1];
const splitType = runtime.workerPaneIds.length === 0 ? "-h" : "-v";
const splitResult = await tmuxExecAsync([
"split-window",
splitType,
"-t",
splitTarget,
"-d",
"-P",
"-F",
"#{pane_id}",
"-c",
runtime.cwd
]);
const paneId = splitResult.stdout.split("\n")[0]?.trim();
if (!paneId) {
try {
await resetTaskToPending(root, taskId, runtime.teamName, runtime.cwd);
} catch {
}
return "";
}
const workerIndex = parseWorkerIndex(workerNameValue);
const agentType = runtime.config.agentTypes[workerIndex % runtime.config.agentTypes.length] ?? runtime.config.agentTypes[0] ?? "claude";
const usePromptMode = isPromptModeAgent(agentType);
const instruction = buildInitialTaskInstruction(runtime.teamName, workerNameValue, task, taskId);
await composeInitialInbox(runtime.teamName, workerNameValue, instruction, runtime.cwd);
const envVars = getWorkerEnv(runtime.teamName, workerNameValue, agentType);
const resolvedBinaryPath = runtime.resolvedBinaryPaths?.[agentType] ?? resolveValidatedBinaryPath(agentType);
if (!runtime.resolvedBinaryPaths) {
runtime.resolvedBinaryPaths = {};
}
runtime.resolvedBinaryPaths[agentType] = resolvedBinaryPath;
const modelForAgent = (() => {
if (agentType === "codex") {
return process.env.OMC_EXTERNAL_MODELS_DEFAULT_CODEX_MODEL || process.env.OMC_CODEX_DEFAULT_MODEL || void 0;
}
if (agentType === "gemini") {
return process.env.OMC_EXTERNAL_MODELS_DEFAULT_GEMINI_MODEL || process.env.OMC_GEMINI_DEFAULT_MODEL || void 0;
}
return resolveClaudeWorkerModel();
})();
const [launchBinary, ...launchArgs] = buildWorkerArgv(agentType, {
teamName: runtime.teamName,
workerName: workerNameValue,
cwd: runtime.cwd,
resolvedBinaryPath,
model: modelForAgent
});
if (usePromptMode) {
const promptArgs = getPromptModeArgs(agentType, generateTriggerMessage(runtime.teamName, workerNameValue));
launchArgs.push(...promptArgs);
}
const paneConfig = {
teamName: runtime.teamName,
workerName: workerNameValue,
envVars,
launchBinary,
launchArgs,
cwd: runtime.cwd
};
await spawnWorkerInPane(runtime.sessionName, paneId, paneConfig);
runtime.workerPaneIds.push(paneId);
runtime.activeWorkers.set(workerNameValue, { paneId, taskId, spawnedAt: Date.now() });
await applyMainVerticalLayout(runtime.sessionName);
try {
await writePanesTrackingFileIfPresent(runtime);
} catch {
}
if (!usePromptMode) {
const paneReady = await waitForPaneReady(paneId);
if (!paneReady) {
await killWorkerPane(runtime, workerNameValue, paneId);
await resetTaskToPending(root, taskId, runtime.teamName, runtime.cwd);
throw new Error(`worker_pane_not_ready:${workerNameValue}`);
}
if (agentType === "gemini") {
const confirmed = await notifyPaneWithRetry(runtime.sessionName, paneId, "1");
if (!confirmed) {
await killWorkerPane(runtime, workerNameValue, paneId);
await resetTaskToPending(root, taskId, runtime.teamName, runtime.cwd);
throw new Error(`worker_notify_failed:${workerNameValue}:trust-confirm`);
}
await new Promise((r) => setTimeout(r, 800));
}
const notified = await notifyPaneWithRetry(
runtime.sessionName,
paneId,
generateTriggerMessage(runtime.teamName, workerNameValue)
);
if (!notified) {
await killWorkerPane(runtime, workerNameValue, paneId);
await resetTaskToPending(root, taskId, runtime.teamName, runtime.cwd);
throw new Error(`worker_notify_failed:${workerNameValue}:initial-inbox`);
}
}
return paneId;
}
async function killWorkerPane(runtime, workerNameValue, paneId) {
try {
await tmuxExecAsync(["kill-pane", "-t", paneId]);
} catch {
}
const paneIndex = runtime.workerPaneIds.indexOf(paneId);
if (paneIndex >= 0) {
runtime.workerPaneIds.splice(paneIndex, 1);
}
runtime.activeWorkers.delete(workerNameValue);
try {
await writePanesTrackingFileIfPresent(runtime);
} catch {
}
}
async function shutdownTeam(teamName, sessionName2, cwd, timeoutMs = 3e4, workerPaneIds, leaderPaneId, ownsWindow) {
const root = stateRoot(cwd, teamName);
await writeJson((0, import_path14.join)(root, "shutdown.json"), {
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
teamName
});
const configData = await readJsonSafe((0, import_path14.join)(root, "config.json"));
const CLI_AGENT_TYPES = /* @__PURE__ */ new Set(["claude", "codex", "gemini"]);
const agentTypes = configData?.agentTypes ?? [];
const isCliWorkerTeam = agentTypes.length > 0 && agentTypes.every((t) => CLI_AGENT_TYPES.has(t));
if (!isCliWorkerTeam) {
const deadline = Date.now() + timeoutMs;
const workerCount = configData?.workerCount ?? 0;
const expectedAcks = Array.from({ length: workerCount }, (_, i) => `worker-${i + 1}`);
while (Date.now() < deadline && expectedAcks.length > 0) {
for (const wName of [...expectedAcks]) {
const ackPath = (0, import_path14.join)(root, "workers", wName, "shutdown-ack.json");
if ((0, import_fs11.existsSync)(ackPath)) {
expectedAcks.splice(expectedAcks.indexOf(wName), 1);
}
}
if (expectedAcks.length > 0) {
await new Promise((r) => setTimeout(r, 500));
}
}
}
const sessionMode = ownsWindow ?? Boolean(configData?.tmuxOwnsWindow) ? sessionName2.includes(":") ? "dedicated-window" : "detached-session" : "split-pane";
const effectiveWorkerPaneIds = sessionMode === "split-pane" ? await resolveSplitPaneWorkerPaneIds(sessionName2, workerPaneIds, leaderPaneId) : workerPaneIds;
await killTeamSession(sessionName2, effectiveWorkerPaneIds, leaderPaneId, { sessionMode });
try {
cleanupTeamWorktrees(teamName, cwd);
} catch {
}
try {
await (0, import_promises3.rm)(root, { recursive: true, force: true });
} catch {
}
}
// src/team/events.ts
var import_crypto = require("crypto");
var import_path15 = require("path");
var import_promises4 = require("fs/promises");
var import_fs12 = require("fs");
// src/lib/swallowed-error.ts
function formatSwallowedError(error) {
if (error instanceof Error) return error.message;
if (typeof error === "string") return error;
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
function logSwallowedError(context, error) {
try {
console.warn(`[omc] ${context}: ${formatSwallowedError(error)}`);
} catch {
}
}
function createSwallowedErrorLogger(context) {
return (error) => {
logSwallowedError(context, error);
};
}
// src/team/events.ts
async function appendTeamEvent(teamName, event, cwd) {
const full = {
event_id: (0, import_crypto.randomUUID)(),
team: teamName,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
...event
};
const p = absPath(cwd, TeamPaths.events(teamName));
await (0, import_promises4.mkdir)((0, import_path15.dirname)(p), { recursive: true });
await (0, import_promises4.appendFile)(p, `${JSON.stringify(full)}
`, "utf8");
return full;
}
async function emitMonitorDerivedEvents(teamName, tasks, workers, previousSnapshot, cwd) {
if (!previousSnapshot) return;
const logDerivedEventFailure = createSwallowedErrorLogger(
"team.events.emitMonitorDerivedEvents appendTeamEvent failed"
);
const completedEventTaskIds = { ...previousSnapshot.completedEventTaskIds ?? {} };
for (const task of tasks) {
const prevStatus = previousSnapshot.taskStatusById?.[task.id];
if (!prevStatus || prevStatus === task.status) continue;
if (task.status === "completed" && !completedEventTaskIds[task.id]) {
await appendTeamEvent(teamName, {
type: "task_completed",
worker: "leader-fixed",
task_id: task.id,
reason: `status_transition:${prevStatus}->${task.status}`
}, cwd).catch(logDerivedEventFailure);
completedEventTaskIds[task.id] = true;
} else if (task.status === "failed") {
await appendTeamEvent(teamName, {
type: "task_failed",
worker: "leader-fixed",
task_id: task.id,
reason: `status_transition:${prevStatus}->${task.status}`
}, cwd).catch(logDerivedEventFailure);
}
}
for (const worker of workers) {
const prevAlive = previousSnapshot.workerAliveByName?.[worker.name];
const prevState = previousSnapshot.workerStateByName?.[worker.name];
if (prevAlive === true && !worker.alive) {
await appendTeamEvent(teamName, {
type: "worker_stopped",
worker: worker.name,
reason: "pane_exited"
}, cwd).catch(logDerivedEventFailure);
}
if (prevState === "working" && worker.status.state === "idle") {
await appendTeamEvent(teamName, {
type: "worker_idle",
worker: worker.name,
reason: `state_transition:${prevState}->${worker.status.state}`
}, cwd).catch(logDerivedEventFailure);
}
}
}
// src/team/leader-nudge-guidance.ts
function activeTaskCount(input) {
return input.tasks.pending + input.tasks.blocked + input.tasks.inProgress;
}
function deriveTeamLeaderGuidance(input) {
const activeTasks = activeTaskCount(input);
const totalWorkers = Math.max(0, input.workers.total);
const aliveWorkers = Math.max(0, input.workers.alive);
const idleWorkers = Math.max(0, input.workers.idle);
const nonReportingWorkers = Math.max(0, input.workers.nonReporting);
if (activeTasks === 0) {
return {
nextAction: "shutdown",
reason: `all_tasks_terminal:completed=${input.tasks.completed},failed=${input.tasks.failed},workers=${totalWorkers}`,
message: "All tasks are in a terminal state. Review any failures, then shut down or clean up the current team."
};
}
if (aliveWorkers === 0) {
return {
nextAction: "launch-new-team",
reason: `no_alive_workers:active=${activeTasks},total_workers=${totalWorkers}`,
message: "Active tasks remain, but no workers appear alive. Launch a new team or replace the dead workers."
};
}
if (idleWorkers >= aliveWorkers) {
return {
nextAction: "reuse-current-team",
reason: `all_alive_workers_idle:active=${activeTasks},alive=${aliveWorkers},idle=${idleWorkers}`,
message: "Workers are idle while active tasks remain. Reuse the current team and reassign, unblock, or restart the pending work."
};
}
if (nonReportingWorkers >= aliveWorkers) {
return {
nextAction: "launch-new-team",
reason: `all_alive_workers_non_reporting:active=${activeTasks},alive=${aliveWorkers},non_reporting=${nonReportingWorkers}`,
message: "Workers are still marked alive, but none are reporting progress. Launch a replacement team or restart the stuck workers."
};
}
return {
nextAction: "keep-checking-status",
reason: `workers_still_active:active=${activeTasks},alive=${aliveWorkers},idle=${idleWorkers},non_reporting=${nonReportingWorkers}`,
message: "Workers still appear active. Keep checking team status before intervening."
};
}
// src/hooks/factcheck/checks.ts
var import_fs13 = require("fs");
var import_path16 = require("path");
// src/hooks/factcheck/types.ts
var REQUIRED_FIELDS = /* @__PURE__ */ new Set([
"schema_version",
"run_id",
"ts",
"cwd",
"mode",
"files_modified",
"files_created",
"artifacts_expected",
"gates"
]);
var REQUIRED_GATES = /* @__PURE__ */ new Set([
"selftest_ran",
"goldens_ran",
"sentinel_stop_smoke_ran",
"shadow_leak_check_ran"
]);
// src/hooks/factcheck/checks.ts
function checkMissingFields(claims) {
const missing = [];
for (const field of REQUIRED_FIELDS) {
if (!(field in claims)) {
missing.push(field);
}
}
return missing.sort();
}
function checkMissingGates(claims) {
const gates = claims.gates ?? {};
const missing = [];
for (const gate of REQUIRED_GATES) {
if (!(gate in gates)) {
missing.push(gate);
}
}
return missing.sort();
}
function getFalseGates(claims) {
const gates = claims.gates ?? {};
const falseGates = [];
for (const gate of REQUIRED_GATES) {
if (gate in gates && !gates[gate]) {
falseGates.push(gate);
}
}
return falseGates.sort();
}
function sourceFileCount(claims) {
const modified = claims.files_modified ?? [];
const created = claims.files_created ?? [];
return modified.length + created.length;
}
function checkPaths(claims, policy) {
const out = [];
const allPaths = [
...claims.files_modified ?? [],
...claims.files_created ?? [],
...claims.artifacts_expected ?? []
];
const deleted = new Set(claims.files_deleted ?? []);
for (const pathStr of allPaths) {
if (deleted.has(pathStr)) continue;
let prefixBlocked = false;
for (const prefix of policy.forbidden_path_prefixes) {
if (pathStr.startsWith(prefix)) {
out.push({ check: "H", severity: "FAIL", detail: `Forbidden path prefix: ${pathStr}` });
prefixBlocked = true;
break;
}
}
if (!prefixBlocked) {
for (const fragment of policy.forbidden_path_substrings) {
if (pathStr.includes(fragment)) {
out.push({ check: "H", severity: "FAIL", detail: `Forbidden path fragment: ${pathStr}` });
break;
}
}
}
if (!(0, import_fs13.existsSync)(pathStr)) {
out.push({ check: "C", severity: "FAIL", detail: `File not found: ${pathStr}` });
}
}
return out;
}
function checkCommands(claims, policy) {
const out = [];
const commands = (claims.commands_executed ?? []).map(String);
for (const cmd of commands) {
const hitPrefix = policy.forbidden_path_prefixes.some(
(forbidden) => cmd.includes(forbidden)
);
if (!hitPrefix) continue;
const stripped = cmd.trim().replace(/^\(/, "");
const isReadOnly = policy.readonly_command_prefixes.some(
(prefix) => stripped.startsWith(prefix)
);
if (!isReadOnly) {
out.push({ check: "H", severity: "FAIL", detail: `Forbidden mutating command: ${cmd}` });
}
}
return out;
}
function checkCwdParity(claimsCwd, runtimeCwd, mode, policy) {
const enforceCwd = policy.warn_on_cwd_mismatch && (mode !== "quick" || policy.enforce_cwd_parity_in_quick);
if (!enforceCwd || !claimsCwd) return null;
const claimsCwdCanonical = (0, import_path16.resolve)(claimsCwd);
const runtimeCwdCanonical = (0, import_path16.resolve)(runtimeCwd);
if (claimsCwdCanonical !== runtimeCwdCanonical) {
const severity = mode === "strict" ? "FAIL" : "WARN";
return {
check: "argv_parity",
severity,
detail: `claims.cwd=${claimsCwdCanonical} runtime.cwd=${runtimeCwdCanonical}`
};
}
return null;
}
// src/hooks/factcheck/config.ts
var import_os3 = require("os");
var DEFAULT_FACTCHECK_POLICY = {
enabled: false,
mode: "quick",
strict_project_patterns: [],
forbidden_path_prefixes: ["${CLAUDE_CONFIG_DIR}/plugins/cache/omc/"],
forbidden_path_substrings: ["/.omc/", ".omc-config.json"],
readonly_command_prefixes: [
"ls ",
"cat ",
"find ",
"grep ",
"head ",
"tail ",
"stat ",
"echo ",
"wc "
],
warn_on_cwd_mismatch: true,
enforce_cwd_parity_in_quick: false,
warn_on_unverified_gates: true,
warn_on_unverified_gates_when_no_source_files: false
};
var DEFAULT_SENTINEL_POLICY = {
enabled: false,
readiness: {
min_pass_rate: 0.6,
max_timeout_rate: 0.1,
max_warn_plus_fail_rate: 0.4,
min_reason_coverage_rate: 0.95
}
};
var DEFAULT_GUARDS_CONFIG = {
factcheck: { ...DEFAULT_FACTCHECK_POLICY },
sentinel: { ...DEFAULT_SENTINEL_POLICY }
};
function expandTokens(value, workspace) {
const home = (0, import_os3.homedir)();
const ws = workspace ?? process.env.OMC_WORKSPACE ?? process.cwd();
return value.replace(/\$\{HOME\}/g, home).replace(/\$\{WORKSPACE\}/g, ws).replace(/\$\{CLAUDE_CONFIG_DIR\}/g, getClaudeConfigDir());
}
function expandTokensDeep(obj, workspace) {
if (typeof obj === "string") {
return expandTokens(obj, workspace);
}
if (Array.isArray(obj)) {
return obj.map((item) => expandTokensDeep(item, workspace));
}
if (typeof obj === "object" && obj !== null) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = expandTokensDeep(value, workspace);
}
return result;
}
return obj;
}
function deepMergeGuards(target, source) {
const result = { ...target };
if (source.factcheck) {
result.factcheck = { ...result.factcheck, ...source.factcheck };
}
if (source.sentinel) {
result.sentinel = {
...result.sentinel,
...source.sentinel,
readiness: {
...result.sentinel.readiness,
...source.sentinel.readiness ?? {}
}
};
}
return result;
}
function loadGuardsConfig(workspace) {
try {
const fullConfig = loadConfig();
const guardsRaw = fullConfig.guards ?? {};
const merged = deepMergeGuards(DEFAULT_GUARDS_CONFIG, guardsRaw);
return expandTokensDeep(merged, workspace);
} catch {
return expandTokensDeep({ ...DEFAULT_GUARDS_CONFIG }, workspace);
}
}
// src/hooks/factcheck/index.ts
function severityRank(value) {
if (value === "FAIL") return 2;
if (value === "WARN") return 1;
return 0;
}
function runChecks(claims, mode, policy, runtimeCwd) {
const mismatches = [];
const notes = [];
const missingFields = checkMissingFields(claims);
if (missingFields.length > 0) {
mismatches.push({
check: "A",
severity: "FAIL",
detail: `Missing required fields: ${JSON.stringify(missingFields)}`
});
}
const missingGates = checkMissingGates(claims);
if (missingGates.length > 0) {
mismatches.push({
check: "A",
severity: "FAIL",
detail: `Missing required gates: ${JSON.stringify(missingGates)}`
});
}
const falseGates = getFalseGates(claims);
const srcFiles = sourceFileCount(claims);
if (mode === "strict" && falseGates.length > 0) {
mismatches.push({
check: "B",
severity: "FAIL",
detail: `Strict mode requires all gates true, got false: ${JSON.stringify(falseGates)}`
});
} else if ((mode === "declared" || mode === "manual") && falseGates.length > 0 && policy.warn_on_unverified_gates) {
if (srcFiles > 0 || policy.warn_on_unverified_gates_when_no_source_files) {
mismatches.push({
check: "B",
severity: "WARN",
detail: `Unverified gates in declared/manual mode: ${JSON.stringify(falseGates)}`
});
} else {
notes.push("No source files declared; unverified gates are ignored by policy");
}
}
mismatches.push(...checkPaths(claims, policy));
mismatches.push(...checkCommands(claims, policy));
const claimsCwd = String(claims.cwd ?? "").trim();
const cwdMismatch = checkCwdParity(
claimsCwd,
runtimeCwd ?? process.cwd(),
mode,
policy
);
if (cwdMismatch) {
mismatches.push(cwdMismatch);
}
const maxRank = mismatches.reduce(
(max, m) => Math.max(max, severityRank(m.severity)),
0
);
let verdict = "PASS";
if (maxRank === 2) verdict = "FAIL";
else if (maxRank === 1) verdict = "WARN";
return {
verdict,
mode,
mismatches,
notes,
claims_evidence: {
source_files: srcFiles,
commands_count: (claims.commands_executed ?? []).length,
models_count: (claims.models_used ?? []).length
}
};
}
function runFactcheck(claims, options) {
const config = loadGuardsConfig(options?.workspace);
const mode = options?.mode ?? config.factcheck.mode;
return runChecks(claims, mode, config.factcheck, options?.runtimeCwd);
}
// src/hooks/factcheck/sentinel.ts
var import_fs14 = require("fs");
function computeRate(numerator, denominator) {
if (denominator === 0) return 0;
return numerator / denominator;
}
function getPassRate(stats) {
return computeRate(stats.pass_count, stats.total_runs);
}
function getTimeoutRate(stats) {
return computeRate(stats.timeout_count, stats.total_runs);
}
function getWarnPlusFailRate(stats) {
return computeRate(stats.warn_count + stats.fail_count, stats.total_runs);
}
function getReasonCoverageRate(stats) {
return computeRate(stats.reason_coverage_count, stats.total_runs);
}
function extractVerdict(entry) {
const raw = String(entry.verdict ?? "").toUpperCase().trim();
if (raw === "PASS") return "PASS";
if (raw === "WARN") return "WARN";
return "FAIL";
}
function hasReason(entry) {
return !!(entry.reason || entry.error || entry.message);
}
function isTimeout(entry) {
if (entry.runtime?.timed_out === true) return true;
if (entry.runtime?.global_timeout === true) return true;
const reason = String(entry.reason ?? "").toLowerCase();
return reason.includes("timeout");
}
function analyzeLog(logPath) {
const stats = {
total_runs: 0,
pass_count: 0,
warn_count: 0,
fail_count: 0,
timeout_count: 0,
reason_coverage_count: 0
};
if (!(0, import_fs14.existsSync)(logPath)) {
return stats;
}
let content;
try {
content = (0, import_fs14.readFileSync)(logPath, "utf-8");
} catch {
return stats;
}
const lines = content.split("\n").filter((line) => line.trim().length > 0);
for (const line of lines) {
let entry;
try {
entry = JSON.parse(line);
} catch {
continue;
}
stats.total_runs++;
const verdict = extractVerdict(entry);
if (verdict === "PASS") stats.pass_count++;
else if (verdict === "WARN") stats.warn_count++;
else stats.fail_count++;
if (isTimeout(entry)) stats.timeout_count++;
if (hasReason(entry)) stats.reason_coverage_count++;
}
return stats;
}
function isUpstreamReady(stats, policy) {
const blockers = [];
const passRate = getPassRate(stats);
if (passRate < policy.min_pass_rate) {
blockers.push(
`pass_rate ${passRate.toFixed(3)} < min ${policy.min_pass_rate}`
);
}
const timeoutRate = getTimeoutRate(stats);
if (timeoutRate > policy.max_timeout_rate) {
blockers.push(
`timeout_rate ${timeoutRate.toFixed(3)} > max ${policy.max_timeout_rate}`
);
}
const warnFailRate = getWarnPlusFailRate(stats);
if (warnFailRate > policy.max_warn_plus_fail_rate) {
blockers.push(
`warn_plus_fail_rate ${warnFailRate.toFixed(3)} > max ${policy.max_warn_plus_fail_rate}`
);
}
const reasonRate = getReasonCoverageRate(stats);
if (reasonRate < policy.min_reason_coverage_rate) {
blockers.push(
`reason_coverage_rate ${reasonRate.toFixed(3)} < min ${policy.min_reason_coverage_rate}`
);
}
return [blockers.length === 0, blockers];
}
function checkSentinelHealth(logPath, workspace) {
const config = loadGuardsConfig(workspace);
const stats = analyzeLog(logPath);
const [ready, blockers] = isUpstreamReady(stats, config.sentinel.readiness);
return { ready, blockers, stats };
}
// src/team/sentinel-gate.ts
function mapFactcheckToBlockers(result) {
if (result.verdict === "PASS") {
return [];
}
if (result.mismatches.length === 0) {
return [`[factcheck] verdict ${result.verdict}`];
}
return result.mismatches.map(
(mismatch) => `[factcheck] ${mismatch.severity} ${mismatch.check}: ${mismatch.detail}`
);
}
function coerceArray(value) {
if (Array.isArray(value)) return value;
if (value == null) return [];
if (typeof value === "object" && !Array.isArray(value)) return [];
return [value];
}
function sanitizeClaims(raw) {
const out = { ...raw };
const arrayFields = [
"files_modified",
"files_created",
"files_deleted",
"artifacts_expected",
"commands_executed",
"models_used"
];
for (const field of arrayFields) {
if (field in out) {
out[field] = coerceArray(out[field]);
}
}
return out;
}
function checkSentinelReadiness(options = {}) {
const {
logPath,
workspace,
claims,
enabled = loadGuardsConfig(workspace).sentinel.enabled
} = options;
if (!enabled) {
return {
ready: true,
blockers: [],
skipped: true
};
}
const blockers = [];
let ranCheck = false;
if (logPath) {
ranCheck = true;
const health = checkSentinelHealth(logPath, workspace);
blockers.push(...health.blockers);
}
if (claims) {
ranCheck = true;
try {
const sanitized = sanitizeClaims(claims);
const factcheck = runFactcheck(sanitized, { workspace });
blockers.push(...mapFactcheckToBlockers(factcheck));
} catch (err) {
blockers.push(
`[factcheck] execution error: ${err instanceof Error ? err.message : String(err)}`
);
}
}
if (!ranCheck) {
return {
ready: false,
blockers: ["[sentinel] gate enabled but no logPath or claims provided \u2014 cannot verify readiness"],
skipped: true
};
}
const dedupedBlockers = [...new Set(blockers)];
return {
ready: dedupedBlockers.length === 0,
blockers: dedupedBlockers,
skipped: false
};
}
async function waitForSentinelReadiness(options = {}) {
const timeoutMs = Math.max(0, options.timeoutMs ?? 3e4);
const pollIntervalMs = Math.max(50, options.pollIntervalMs ?? 250);
const startedAt = Date.now();
let attempts = 1;
let latest = checkSentinelReadiness(options);
if (latest.ready) {
return {
...latest,
timedOut: false,
elapsedMs: Date.now() - startedAt,
attempts
};
}
const deadline = startedAt + timeoutMs;
while (Date.now() < deadline) {
await new Promise((resolve5) => setTimeout(resolve5, pollIntervalMs));
attempts += 1;
latest = checkSentinelReadiness(options);
if (latest.ready) {
return {
...latest,
timedOut: false,
elapsedMs: Date.now() - startedAt,
attempts
};
}
}
const timeoutBlocker = `[sentinel] readiness check timed out after ${timeoutMs}ms`;
const blockers = latest.blockers.includes(timeoutBlocker) ? latest.blockers : [...latest.blockers, timeoutBlocker];
return {
...latest,
blockers,
timedOut: true,
elapsedMs: Date.now() - startedAt,
attempts
};
}
// src/team/runtime-v2.ts
init_tmux_utils();
var import_path19 = require("path");
var import_fs17 = require("fs");
var import_promises7 = require("fs/promises");
var import_perf_hooks = require("perf_hooks");
// src/team/allocation-policy.ts
function allocateTasksToWorkers(tasks, workers) {
if (tasks.length === 0 || workers.length === 0) return [];
const uniformRolePool = isUniformRolePool(workers);
const results = [];
const loadMap = new Map(workers.map((w) => [w.name, w.currentLoad]));
if (uniformRolePool) {
for (const task of tasks) {
const target = pickLeastLoaded(workers, loadMap);
results.push({
taskId: task.id,
workerName: target.name,
reason: `uniform pool round-robin (role=${target.role}, load=${loadMap.get(target.name)})`
});
loadMap.set(target.name, (loadMap.get(target.name) ?? 0) + 1);
}
} else {
for (const task of tasks) {
const target = pickBestWorker(task, workers, loadMap);
results.push({
taskId: task.id,
workerName: target.name,
reason: `role match (task.role=${task.role ?? "any"}, worker.role=${target.role}, load=${loadMap.get(target.name)})`
});
loadMap.set(target.name, (loadMap.get(target.name) ?? 0) + 1);
}
}
return results;
}
function isUniformRolePool(workers) {
if (workers.length === 0) return true;
const firstRole = workers[0].role;
return workers.every((w) => w.role === firstRole);
}
function pickLeastLoaded(workers, loadMap) {
let best = workers[0];
let bestLoad = loadMap.get(best.name) ?? 0;
for (const w of workers) {
const load = loadMap.get(w.name) ?? 0;
if (load < bestLoad) {
best = w;
bestLoad = load;
}
}
return best;
}
function pickBestWorker(task, workers, loadMap) {
const scored = workers.map((w) => {
const load = loadMap.get(w.name) ?? 0;
const roleScore = task.role ? w.role === task.role ? 1 : 0 : 0.5;
const score = roleScore - load * 0.2;
return { worker: w, score };
});
scored.sort((a, b) => b.score - a.score);
return scored[0].worker;
}
// src/team/monitor.ts
var import_fs15 = require("fs");
var import_promises5 = require("fs/promises");
var import_path17 = require("path");
// src/team/governance.ts
var DEFAULT_TEAM_TRANSPORT_POLICY = {
display_mode: "split_pane",
worker_launch_mode: "interactive",
dispatch_mode: "hook_preferred_with_fallback",
dispatch_ack_timeout_ms: 15e3
};
var DEFAULT_TEAM_GOVERNANCE = {
delegation_only: false,
plan_approval_required: false,
nested_teams_allowed: false,
one_team_per_leader_session: true,
cleanup_requires_all_workers_inactive: true
};
function normalizeTeamTransportPolicy(policy) {
return {
display_mode: policy?.display_mode ?? DEFAULT_TEAM_TRANSPORT_POLICY.display_mode,
worker_launch_mode: policy?.worker_launch_mode ?? DEFAULT_TEAM_TRANSPORT_POLICY.worker_launch_mode,
dispatch_mode: policy?.dispatch_mode ?? DEFAULT_TEAM_TRANSPORT_POLICY.dispatch_mode,
dispatch_ack_timeout_ms: typeof policy?.dispatch_ack_timeout_ms === "number" ? policy.dispatch_ack_timeout_ms : DEFAULT_TEAM_TRANSPORT_POLICY.dispatch_ack_timeout_ms
};
}
function normalizeTeamGovernance(governance, legacyPolicy) {
return {
delegation_only: governance?.delegation_only ?? legacyPolicy?.delegation_only ?? DEFAULT_TEAM_GOVERNANCE.delegation_only,
plan_approval_required: governance?.plan_approval_required ?? legacyPolicy?.plan_approval_required ?? DEFAULT_TEAM_GOVERNANCE.plan_approval_required,
nested_teams_allowed: governance?.nested_teams_allowed ?? legacyPolicy?.nested_teams_allowed ?? DEFAULT_TEAM_GOVERNANCE.nested_teams_allowed,
one_team_per_leader_session: governance?.one_team_per_leader_session ?? legacyPolicy?.one_team_per_leader_session ?? DEFAULT_TEAM_GOVERNANCE.one_team_per_leader_session,
cleanup_requires_all_workers_inactive: governance?.cleanup_requires_all_workers_inactive ?? legacyPolicy?.cleanup_requires_all_workers_inactive ?? DEFAULT_TEAM_GOVERNANCE.cleanup_requires_all_workers_inactive
};
}
function normalizeTeamManifest(manifest) {
return {
...manifest,
policy: normalizeTeamTransportPolicy(manifest.policy),
governance: normalizeTeamGovernance(manifest.governance, manifest.policy)
};
}
function getConfigGovernance(config) {
return normalizeTeamGovernance(config?.governance, config?.policy);
}
// src/team/worker-canonicalization.ts
function hasText(value) {
return typeof value === "string" && value.trim().length > 0;
}
function hasAssignedTasks(worker) {
return Array.isArray(worker.assigned_tasks) && worker.assigned_tasks.length > 0;
}
function workerPriority(worker) {
if (hasText(worker.pane_id)) return 4;
if (typeof worker.pid === "number" && Number.isFinite(worker.pid)) return 3;
if (hasAssignedTasks(worker)) return 2;
if (typeof worker.index === "number" && worker.index > 0) return 1;
return 0;
}
function mergeAssignedTasks(primary, secondary) {
const merged = [];
for (const taskId of [...primary ?? [], ...secondary ?? []]) {
if (typeof taskId !== "string" || taskId.trim() === "" || merged.includes(taskId)) continue;
merged.push(taskId);
}
return merged;
}
function backfillText(primary, secondary) {
return hasText(primary) ? primary : secondary;
}
function backfillBoolean(primary, secondary) {
return typeof primary === "boolean" ? primary : secondary;
}
function backfillNumber(primary, secondary, predicate) {
const isUsable = (value) => typeof value === "number" && Number.isFinite(value) && (predicate ? predicate(value) : true);
return isUsable(primary) ? primary : isUsable(secondary) ? secondary : void 0;
}
function chooseWinningWorker(existing, incoming) {
const existingPriority = workerPriority(existing);
const incomingPriority = workerPriority(incoming);
if (incomingPriority > existingPriority) return { winner: incoming, loser: existing };
if (incomingPriority < existingPriority) return { winner: existing, loser: incoming };
if ((incoming.index ?? 0) >= (existing.index ?? 0)) return { winner: incoming, loser: existing };
return { winner: existing, loser: incoming };
}
function canonicalizeWorkers(workers) {
const byName = /* @__PURE__ */ new Map();
const duplicateNames = /* @__PURE__ */ new Set();
for (const worker of workers) {
const name = typeof worker.name === "string" ? worker.name.trim() : "";
if (!name) continue;
const normalized = {
...worker,
name,
assigned_tasks: Array.isArray(worker.assigned_tasks) ? worker.assigned_tasks : []
};
const existing = byName.get(name);
if (!existing) {
byName.set(name, normalized);
continue;
}
duplicateNames.add(name);
const { winner, loser } = chooseWinningWorker(existing, normalized);
byName.set(name, {
...winner,
name,
assigned_tasks: mergeAssignedTasks(winner.assigned_tasks, loser.assigned_tasks),
pane_id: backfillText(winner.pane_id, loser.pane_id),
pid: backfillNumber(winner.pid, loser.pid),
index: backfillNumber(winner.index, loser.index, (value) => value > 0) ?? 0,
role: backfillText(winner.role, loser.role) ?? winner.role,
worker_cli: backfillText(winner.worker_cli, loser.worker_cli),
working_dir: backfillText(winner.working_dir, loser.working_dir),
worktree_path: backfillText(winner.worktree_path, loser.worktree_path),
worktree_branch: backfillText(winner.worktree_branch, loser.worktree_branch),
worktree_detached: backfillBoolean(winner.worktree_detached, loser.worktree_detached),
team_state_root: backfillText(winner.team_state_root, loser.team_state_root)
});
}
return {
workers: Array.from(byName.values()),
duplicateNames: Array.from(duplicateNames.values())
};
}
function canonicalizeTeamConfigWorkers(config) {
const { workers, duplicateNames } = canonicalizeWorkers(config.workers ?? []);
if (duplicateNames.length > 0) {
console.warn(
`[team] canonicalized duplicate worker entries: ${duplicateNames.join(", ")}`
);
}
return {
...config,
workers,
worker_count: workers.length
};
}
// src/team/monitor.ts
async function readJsonSafe2(filePath) {
try {
if (!(0, import_fs15.existsSync)(filePath)) return null;
const raw = await (0, import_promises5.readFile)(filePath, "utf-8");
return JSON.parse(raw);
} catch {
return null;
}
}
async function writeAtomic(filePath, data) {
const { writeFile: writeFile6 } = await import("fs/promises");
await (0, import_promises5.mkdir)((0, import_path17.dirname)(filePath), { recursive: true });
const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
await writeFile6(tmpPath, data, "utf-8");
const { rename: rename4 } = await import("fs/promises");
await rename4(tmpPath, filePath);
}
function configFromManifest(manifest) {
return {
name: manifest.name,
task: manifest.task,
agent_type: "claude",
policy: manifest.policy,
governance: manifest.governance,
worker_launch_mode: manifest.policy.worker_launch_mode,
worker_count: manifest.worker_count,
max_workers: 20,
workers: manifest.workers,
created_at: manifest.created_at,
tmux_session: manifest.tmux_session,
next_task_id: manifest.next_task_id,
leader_cwd: manifest.leader_cwd,
team_state_root: manifest.team_state_root,
workspace_mode: manifest.workspace_mode,
leader_pane_id: manifest.leader_pane_id,
hud_pane_id: manifest.hud_pane_id,
resize_hook_name: manifest.resize_hook_name,
resize_hook_target: manifest.resize_hook_target,
next_worker_index: manifest.next_worker_index
};
}
async function readTeamConfig(teamName, cwd) {
const [config, manifest] = await Promise.all([
readJsonSafe2(absPath(cwd, TeamPaths.config(teamName))),
readTeamManifest(teamName, cwd)
]);
if (!config && !manifest) return null;
if (!manifest) return config ? canonicalizeTeamConfigWorkers(config) : null;
if (!config) return canonicalizeTeamConfigWorkers(configFromManifest(manifest));
return canonicalizeTeamConfigWorkers({
...configFromManifest(manifest),
...config,
workers: [...config.workers ?? [], ...manifest.workers ?? []],
worker_count: Math.max(config.worker_count ?? 0, manifest.worker_count ?? 0),
next_task_id: Math.max(config.next_task_id ?? 1, manifest.next_task_id ?? 1),
max_workers: Math.max(config.max_workers ?? 0, 20)
});
}
async function readTeamManifest(teamName, cwd) {
const manifest = await readJsonSafe2(absPath(cwd, TeamPaths.manifest(teamName)));
return manifest ? normalizeTeamManifest(manifest) : null;
}
async function readWorkerStatus(teamName, workerName2, cwd) {
const data = await readJsonSafe2(absPath(cwd, TeamPaths.workerStatus(teamName, workerName2)));
return data ?? { state: "unknown", updated_at: "" };
}
async function readWorkerHeartbeat(teamName, workerName2, cwd) {
return readJsonSafe2(absPath(cwd, TeamPaths.heartbeat(teamName, workerName2)));
}
async function readMonitorSnapshot(teamName, cwd) {
const p = absPath(cwd, TeamPaths.monitorSnapshot(teamName));
if (!(0, import_fs15.existsSync)(p)) return null;
try {
const raw = await (0, import_promises5.readFile)(p, "utf-8");
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object") return null;
const monitorTimings = (() => {
const candidate = parsed.monitorTimings;
if (!candidate || typeof candidate !== "object") return void 0;
if (typeof candidate.list_tasks_ms !== "number" || typeof candidate.worker_scan_ms !== "number" || typeof candidate.mailbox_delivery_ms !== "number" || typeof candidate.total_ms !== "number" || typeof candidate.updated_at !== "string") {
return void 0;
}
return candidate;
})();
return {
taskStatusById: parsed.taskStatusById ?? {},
workerAliveByName: parsed.workerAliveByName ?? {},
workerStateByName: parsed.workerStateByName ?? {},
workerTurnCountByName: parsed.workerTurnCountByName ?? {},
workerTaskIdByName: parsed.workerTaskIdByName ?? {},
mailboxNotifiedByMessageId: parsed.mailboxNotifiedByMessageId ?? {},
completedEventTaskIds: parsed.completedEventTaskIds ?? {},
monitorTimings
};
} catch {
return null;
}
}
async function writeMonitorSnapshot(teamName, snapshot, cwd) {
await writeAtomic(absPath(cwd, TeamPaths.monitorSnapshot(teamName)), JSON.stringify(snapshot, null, 2));
}
async function writeShutdownRequest(teamName, workerName2, fromWorker, cwd) {
const data = {
from: fromWorker,
requested_at: (/* @__PURE__ */ new Date()).toISOString()
};
await writeAtomic(absPath(cwd, TeamPaths.shutdownRequest(teamName, workerName2)), JSON.stringify(data, null, 2));
}
async function readShutdownAck(teamName, workerName2, cwd, requestedAfter) {
const ack = await readJsonSafe2(
absPath(cwd, TeamPaths.shutdownAck(teamName, workerName2))
);
if (!ack) return null;
if (requestedAfter && ack.updated_at) {
if (new Date(ack.updated_at).getTime() < new Date(requestedAfter).getTime()) {
return null;
}
}
return ack;
}
async function listTasksFromFiles(teamName, cwd) {
const tasksDir = absPath(cwd, TeamPaths.tasks(teamName));
if (!(0, import_fs15.existsSync)(tasksDir)) return [];
const { readdir: readdir2 } = await import("fs/promises");
const entries = await readdir2(tasksDir);
const tasks = [];
for (const entry of entries) {
const match = /^(?:task-)?(\d+)\.json$/.exec(entry);
if (!match) continue;
const task = await readJsonSafe2(absPath(cwd, `${TeamPaths.tasks(teamName)}/${entry}`));
if (task) tasks.push(task);
}
return tasks.sort((a, b) => Number(a.id) - Number(b.id));
}
async function writeWorkerInbox(teamName, workerName2, content, cwd) {
await writeAtomic(absPath(cwd, TeamPaths.inbox(teamName, workerName2)), content);
}
async function saveTeamConfig(config, cwd) {
await writeAtomic(absPath(cwd, TeamPaths.config(config.name)), JSON.stringify(config, null, 2));
const manifestPath = absPath(cwd, TeamPaths.manifest(config.name));
const existingManifest = await readJsonSafe2(manifestPath);
if (existingManifest) {
const nextManifest = normalizeTeamManifest({
...existingManifest,
workers: config.workers,
worker_count: config.worker_count,
tmux_session: config.tmux_session,
next_task_id: config.next_task_id,
created_at: config.created_at,
leader_cwd: config.leader_cwd,
team_state_root: config.team_state_root,
workspace_mode: config.workspace_mode,
leader_pane_id: config.leader_pane_id,
hud_pane_id: config.hud_pane_id,
resize_hook_name: config.resize_hook_name,
resize_hook_target: config.resize_hook_target,
next_worker_index: config.next_worker_index,
policy: config.policy ?? existingManifest.policy,
governance: config.governance ?? existingManifest.governance
});
await writeAtomic(manifestPath, JSON.stringify(nextManifest, null, 2));
}
}
async function cleanupTeamState(teamName, cwd) {
const root = absPath(cwd, TeamPaths.root(teamName));
const { rm: rm3 } = await import("fs/promises");
try {
await rm3(root, { recursive: true, force: true });
} catch {
}
}
// src/team/phase-controller.ts
function inferPhase(tasks) {
if (tasks.length === 0) return "initializing";
const inProgress = tasks.filter((t) => t.status === "in_progress");
const pending = tasks.filter((t) => t.status === "pending");
const permanentlyFailed = tasks.filter(
(t) => t.status === "completed" && t.metadata?.permanentlyFailed === true
);
const genuinelyCompleted = tasks.filter(
(t) => t.status === "completed" && !t.metadata?.permanentlyFailed
);
const explicitlyFailed = tasks.filter((t) => t.status === "failed");
const allFailed = [...permanentlyFailed, ...explicitlyFailed];
if (inProgress.length > 0) return "executing";
if (pending.length === tasks.length && genuinelyCompleted.length === 0 && allFailed.length === 0) {
return "planning";
}
if (pending.length > 0 && genuinelyCompleted.length > 0 && inProgress.length === 0 && allFailed.length === 0) {
return "executing";
}
if (allFailed.length > 0) {
const hasRetriesRemaining = allFailed.some((t) => {
const retryCount = t.metadata?.retryCount ?? 0;
const maxRetries = t.metadata?.maxRetries ?? 3;
return retryCount < maxRetries;
});
if (allFailed.length === tasks.length && !hasRetriesRemaining || pending.length === 0 && inProgress.length === 0 && genuinelyCompleted.length === 0 && !hasRetriesRemaining) {
return "failed";
}
if (hasRetriesRemaining) return "fixing";
}
if (genuinelyCompleted.length === tasks.length && allFailed.length === 0) {
return "completed";
}
return "executing";
}
// src/team/runtime-v2.ts
init_team_name();
init_tmux_session();
// src/team/dispatch-queue.ts
var import_crypto2 = require("crypto");
var import_fs16 = require("fs");
var import_promises6 = require("fs/promises");
var import_path18 = require("path");
// src/team/contracts.ts
var WORKER_NAME_SAFE_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
// src/team/dispatch-queue.ts
var OMC_DISPATCH_LOCK_TIMEOUT_ENV = "OMC_TEAM_DISPATCH_LOCK_TIMEOUT_MS";
var DEFAULT_DISPATCH_LOCK_TIMEOUT_MS = 15e3;
var MIN_DISPATCH_LOCK_TIMEOUT_MS = 1e3;
var MAX_DISPATCH_LOCK_TIMEOUT_MS = 12e4;
var DISPATCH_LOCK_INITIAL_POLL_MS = 25;
var DISPATCH_LOCK_MAX_POLL_MS = 500;
var LOCK_STALE_MS = 5 * 60 * 1e3;
function validateWorkerName(name) {
if (!WORKER_NAME_SAFE_PATTERN.test(name)) {
throw new Error(`Invalid worker name: "${name}"`);
}
}
function isDispatchKind(value) {
return value === "inbox" || value === "mailbox" || value === "nudge";
}
function isDispatchStatus(value) {
return value === "pending" || value === "notified" || value === "delivered" || value === "failed";
}
function resolveDispatchLockTimeoutMs(env = process.env) {
const raw = env[OMC_DISPATCH_LOCK_TIMEOUT_ENV];
if (raw === void 0 || raw === "") return DEFAULT_DISPATCH_LOCK_TIMEOUT_MS;
const parsed = Number(raw);
if (!Number.isFinite(parsed)) return DEFAULT_DISPATCH_LOCK_TIMEOUT_MS;
return Math.max(MIN_DISPATCH_LOCK_TIMEOUT_MS, Math.min(MAX_DISPATCH_LOCK_TIMEOUT_MS, Math.floor(parsed)));
}
async function withDispatchLock(teamName, cwd, fn) {
const root = absPath(cwd, TeamPaths.root(teamName));
if (!(0, import_fs16.existsSync)(root)) throw new Error(`Team ${teamName} not found`);
const lockDir = absPath(cwd, TeamPaths.dispatchLockDir(teamName));
const ownerPath = (0, import_path18.join)(lockDir, "owner");
const ownerToken = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
const timeoutMs = resolveDispatchLockTimeoutMs(process.env);
const deadline = Date.now() + timeoutMs;
let pollMs = DISPATCH_LOCK_INITIAL_POLL_MS;
await (0, import_promises6.mkdir)((0, import_path18.dirname)(lockDir), { recursive: true });
while (true) {
try {
await (0, import_promises6.mkdir)(lockDir, { recursive: false });
try {
await (0, import_promises6.writeFile)(ownerPath, ownerToken, "utf8");
} catch (error) {
await (0, import_promises6.rm)(lockDir, { recursive: true, force: true });
throw error;
}
break;
} catch (error) {
const err = error;
if (err.code !== "EEXIST") throw error;
try {
const info = await (0, import_promises6.stat)(lockDir);
if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
await (0, import_promises6.rm)(lockDir, { recursive: true, force: true });
continue;
}
} catch {
}
if (Date.now() > deadline) {
throw new Error(
`Timed out acquiring dispatch lock for ${teamName} after ${timeoutMs}ms. Set ${OMC_DISPATCH_LOCK_TIMEOUT_ENV} to increase (current: ${timeoutMs}ms, max: ${MAX_DISPATCH_LOCK_TIMEOUT_MS}ms).`
);
}
const jitter = 0.5 + Math.random() * 0.5;
await new Promise((resolve5) => setTimeout(resolve5, Math.floor(pollMs * jitter)));
pollMs = Math.min(pollMs * 2, DISPATCH_LOCK_MAX_POLL_MS);
}
}
try {
return await fn();
} finally {
try {
const currentOwner = await (0, import_promises6.readFile)(ownerPath, "utf8");
if (currentOwner.trim() === ownerToken) {
await (0, import_promises6.rm)(lockDir, { recursive: true, force: true });
}
} catch {
}
}
}
async function readDispatchRequestsFromFile(teamName, cwd) {
const path4 = absPath(cwd, TeamPaths.dispatchRequests(teamName));
try {
if (!(0, import_fs16.existsSync)(path4)) return [];
const raw = await (0, import_promises6.readFile)(path4, "utf8");
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.map((entry) => normalizeDispatchRequest(teamName, entry)).filter((req) => req !== null);
} catch {
return [];
}
}
async function writeDispatchRequestsToFile(teamName, requests, cwd) {
const path4 = absPath(cwd, TeamPaths.dispatchRequests(teamName));
const dir = (0, import_path18.dirname)(path4);
ensureDirWithMode(dir);
atomicWriteJson(path4, requests);
}
function normalizeDispatchRequest(teamName, raw, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
if (!isDispatchKind(raw.kind)) return null;
if (typeof raw.to_worker !== "string" || raw.to_worker.trim() === "") return null;
if (typeof raw.trigger_message !== "string" || raw.trigger_message.trim() === "") return null;
const status = isDispatchStatus(raw.status) ? raw.status : "pending";
return {
request_id: typeof raw.request_id === "string" && raw.request_id.trim() !== "" ? raw.request_id : (0, import_crypto2.randomUUID)(),
kind: raw.kind,
team_name: teamName,
to_worker: raw.to_worker,
worker_index: typeof raw.worker_index === "number" ? raw.worker_index : void 0,
pane_id: typeof raw.pane_id === "string" && raw.pane_id !== "" ? raw.pane_id : void 0,
trigger_message: raw.trigger_message,
message_id: typeof raw.message_id === "string" && raw.message_id !== "" ? raw.message_id : void 0,
inbox_correlation_key: typeof raw.inbox_correlation_key === "string" && raw.inbox_correlation_key !== "" ? raw.inbox_correlation_key : void 0,
transport_preference: raw.transport_preference === "transport_direct" || raw.transport_preference === "prompt_stdin" ? raw.transport_preference : "hook_preferred_with_fallback",
fallback_allowed: raw.fallback_allowed !== false,
status,
attempt_count: Number.isFinite(raw.attempt_count) ? Math.max(0, Math.floor(raw.attempt_count)) : 0,
created_at: typeof raw.created_at === "string" && raw.created_at !== "" ? raw.created_at : nowIso,
updated_at: typeof raw.updated_at === "string" && raw.updated_at !== "" ? raw.updated_at : nowIso,
notified_at: typeof raw.notified_at === "string" && raw.notified_at !== "" ? raw.notified_at : void 0,
delivered_at: typeof raw.delivered_at === "string" && raw.delivered_at !== "" ? raw.delivered_at : void 0,
failed_at: typeof raw.failed_at === "string" && raw.failed_at !== "" ? raw.failed_at : void 0,
last_reason: typeof raw.last_reason === "string" && raw.last_reason !== "" ? raw.last_reason : void 0
};
}
function equivalentPendingDispatch(existing, input) {
if (existing.status !== "pending") return false;
if (existing.kind !== input.kind) return false;
if (existing.to_worker !== input.to_worker) return false;
if (input.kind === "mailbox") {
return Boolean(input.message_id) && existing.message_id === input.message_id;
}
if (input.kind === "inbox" && input.inbox_correlation_key) {
return existing.inbox_correlation_key === input.inbox_correlation_key;
}
return existing.trigger_message === input.trigger_message;
}
function canTransitionDispatchStatus(from, to) {
if (from === to) return true;
if (from === "pending" && (to === "notified" || to === "failed")) return true;
if (from === "notified" && (to === "delivered" || to === "failed")) return true;
return false;
}
async function enqueueDispatchRequest(teamName, requestInput, cwd) {
if (!isDispatchKind(requestInput.kind)) throw new Error(`Invalid dispatch request kind: ${String(requestInput.kind)}`);
if (requestInput.kind === "mailbox" && (!requestInput.message_id || requestInput.message_id.trim() === "")) {
throw new Error("mailbox dispatch requests require message_id");
}
validateWorkerName(requestInput.to_worker);
return await withDispatchLock(teamName, cwd, async () => {
const requests = await readDispatchRequestsFromFile(teamName, cwd);
const existing = requests.find((req) => equivalentPendingDispatch(req, requestInput));
if (existing) return { request: existing, deduped: true };
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
const request = normalizeDispatchRequest(
teamName,
{
request_id: (0, import_crypto2.randomUUID)(),
...requestInput,
status: "pending",
attempt_count: 0,
created_at: nowIso,
updated_at: nowIso
},
nowIso
);
if (!request) throw new Error("failed_to_normalize_dispatch_request");
requests.push(request);
await writeDispatchRequestsToFile(teamName, requests, cwd);
return { request, deduped: false };
});
}
async function readDispatchRequest(teamName, requestId, cwd) {
const requests = await readDispatchRequestsFromFile(teamName, cwd);
return requests.find((req) => req.request_id === requestId) ?? null;
}
async function transitionDispatchRequest(teamName, requestId, from, to, patch = {}, cwd) {
return await withDispatchLock(teamName, cwd, async () => {
const requests = await readDispatchRequestsFromFile(teamName, cwd);
const index = requests.findIndex((req) => req.request_id === requestId);
if (index < 0) return null;
const existing = requests[index];
if (existing.status !== from && existing.status !== to) return null;
if (!canTransitionDispatchStatus(existing.status, to)) return null;
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
const nextAttemptCount = Math.max(
existing.attempt_count,
Number.isFinite(patch.attempt_count) ? Math.floor(patch.attempt_count) : existing.status === to ? existing.attempt_count : existing.attempt_count + 1
);
const next = {
...existing,
...patch,
status: to,
attempt_count: Math.max(0, nextAttemptCount),
updated_at: nowIso
};
if (to === "notified") next.notified_at = patch.notified_at ?? nowIso;
if (to === "delivered") next.delivered_at = patch.delivered_at ?? nowIso;
if (to === "failed") next.failed_at = patch.failed_at ?? nowIso;
requests[index] = next;
await writeDispatchRequestsToFile(teamName, requests, cwd);
return next;
});
}
async function markDispatchRequestNotified(teamName, requestId, patch = {}, cwd) {
const current = await readDispatchRequest(teamName, requestId, cwd);
if (!current) return null;
if (current.status === "notified" || current.status === "delivered") return current;
return await transitionDispatchRequest(teamName, requestId, current.status, "notified", patch, cwd);
}
// src/team/mcp-comm.ts
function isConfirmedNotification(outcome) {
if (!outcome.ok) return false;
if (outcome.transport !== "hook") return true;
return outcome.reason !== "queued_for_hook_dispatch";
}
function fallbackTransportForPreference(preference) {
if (preference === "prompt_stdin") return "prompt_stdin";
if (preference === "transport_direct") return "tmux_send_keys";
return "hook";
}
function notifyExceptionReason(error) {
const message = error instanceof Error ? error.message : String(error);
return `notify_exception:${message}`;
}
async function markImmediateDispatchFailure(params) {
const { teamName, request, reason, messageId, cwd } = params;
if (request.transport_preference === "hook_preferred_with_fallback") return;
const logTransitionFailure = createSwallowedErrorLogger(
"team.mcp-comm.markImmediateDispatchFailure transitionDispatchRequest failed"
);
const current = await readDispatchRequest(teamName, request.request_id, cwd);
if (!current) return;
if (current.status === "failed" || current.status === "notified" || current.status === "delivered") return;
await transitionDispatchRequest(
teamName,
request.request_id,
current.status,
"failed",
{
message_id: messageId ?? current.message_id,
last_reason: reason
},
cwd
).catch(logTransitionFailure);
}
async function queueInboxInstruction(params) {
const queued = await enqueueDispatchRequest(
params.teamName,
{
kind: "inbox",
to_worker: params.workerName,
worker_index: params.workerIndex,
pane_id: params.paneId,
trigger_message: params.triggerMessage,
transport_preference: params.transportPreference,
fallback_allowed: params.fallbackAllowed,
inbox_correlation_key: params.inboxCorrelationKey
},
params.cwd
);
if (queued.deduped) {
return {
ok: false,
transport: "none",
reason: "duplicate_pending_dispatch_request",
request_id: queued.request.request_id
};
}
try {
await params.deps.writeWorkerInbox(params.teamName, params.workerName, params.inbox, params.cwd);
} catch (error) {
await markImmediateDispatchFailure({
teamName: params.teamName,
request: queued.request,
reason: "inbox_write_failed",
cwd: params.cwd
});
throw error;
}
const notifyOutcome = await Promise.resolve(params.notify(
{ workerName: params.workerName, workerIndex: params.workerIndex, paneId: params.paneId },
params.triggerMessage,
{ request: queued.request }
)).catch((error) => ({
ok: false,
transport: fallbackTransportForPreference(params.transportPreference),
reason: notifyExceptionReason(error)
}));
const outcome = { ...notifyOutcome, request_id: queued.request.request_id };
if (isConfirmedNotification(outcome)) {
await markDispatchRequestNotified(
params.teamName,
queued.request.request_id,
{ last_reason: outcome.reason },
params.cwd
);
} else {
await markImmediateDispatchFailure({
teamName: params.teamName,
request: queued.request,
reason: outcome.reason,
cwd: params.cwd
});
}
return outcome;
}
// src/team/stage-router.ts
var ROLE_TO_AGENT = {
orchestrator: "omc",
planner: "planner",
analyst: "analyst",
architect: "architect",
executor: "executor",
debugger: "debugger",
critic: "critic",
"code-reviewer": "codeReviewer",
"security-reviewer": "securityReviewer",
"test-engineer": "testEngineer",
designer: "designer",
writer: "writer",
"code-simplifier": "codeSimplifier",
explore: "explore",
"document-specialist": "documentSpecialist"
};
var ROLE_DEFAULT_TIER = {
orchestrator: "HIGH",
planner: "HIGH",
analyst: "HIGH",
architect: "HIGH",
executor: "MEDIUM",
debugger: "MEDIUM",
critic: "HIGH",
"code-reviewer": "HIGH",
"security-reviewer": "MEDIUM",
"test-engineer": "MEDIUM",
designer: "MEDIUM",
writer: "LOW",
"code-simplifier": "HIGH",
explore: "LOW",
"document-specialist": "MEDIUM"
};
var TIER_SET = /* @__PURE__ */ new Set(["HIGH", "MEDIUM", "LOW"]);
function isTier(value) {
return TIER_SET.has(value);
}
function getRoleRoutingSpec(roleRouting, role) {
if (!roleRouting) return void 0;
const normalizedRole = normalizeDelegationRole(role);
const direct = roleRouting[normalizedRole];
if (direct) return direct;
for (const [rawRole, spec] of Object.entries(roleRouting)) {
if (spec && normalizeDelegationRole(rawRole) === normalizedRole) {
return spec;
}
}
return void 0;
}
function resolveTierToModelId(tier, cfg) {
const fromCfg = cfg.routing?.tierModels?.[tier];
if (typeof fromCfg === "string" && fromCfg.length > 0) return fromCfg;
return getDefaultTierModels()[tier];
}
function resolveClaudeModel(role, raw, cfg) {
if (typeof raw === "string" && raw.length > 0) {
return isTier(raw) ? resolveTierToModelId(raw, cfg) : raw;
}
return resolveTierToModelId(ROLE_DEFAULT_TIER[role], cfg);
}
function resolveExternalModel(provider, raw, cfg) {
if (typeof raw === "string" && raw.length > 0 && !isTier(raw)) {
return raw;
}
const defaults = cfg.externalModels?.defaults;
if (provider === "codex") {
return defaults?.codexModel ?? BUILTIN_EXTERNAL_MODEL_DEFAULTS.codexModel;
}
return defaults?.geminiModel ?? BUILTIN_EXTERNAL_MODEL_DEFAULTS.geminiModel;
}
function resolveRoleAssignment(role, cfg) {
const normalized = normalizeDelegationRole(role);
const canonical = isCanonicalRole(normalized) ? normalized : role;
const roleRouting = cfg.team?.roleRouting;
const spec = getRoleRoutingSpec(roleRouting, canonical);
const isOrchestrator = canonical === "orchestrator";
const provider = isOrchestrator ? "claude" : spec?.provider ?? "claude";
const model = provider === "claude" ? resolveClaudeModel(canonical, spec?.model, cfg) : resolveExternalModel(provider, spec?.model, cfg);
const agent = spec?.agent ?? ROLE_TO_AGENT[canonical];
return { provider, model, agent };
}
function isCanonicalRole(value) {
return CANONICAL_TEAM_ROLES.includes(value);
}
function buildResolvedRoutingSnapshot(cfg) {
const out = {};
const roleRouting = cfg.team?.roleRouting;
for (const role of CANONICAL_TEAM_ROLES) {
const primary = resolveRoleAssignment(role, cfg);
const spec = getRoleRoutingSpec(roleRouting, role);
const isExternalPrimary = primary.provider !== "claude";
const fallbackModelInput = isExternalPrimary && spec?.model && !isTier(spec.model) ? void 0 : spec?.model;
const fallback = {
provider: "claude",
model: resolveClaudeModel(role, fallbackModelInput, cfg),
agent: primary.agent
};
out[role] = { primary, fallback };
}
return out;
}
// src/team/role-router.ts
var INTENT_PATTERNS = [
{
intent: "build-fix",
patterns: [
/\bfix(?:ing)?\s+(?:the\s+)?(?:build|ci|lint|compile|tsc|type.?check)/i,
/\bfailing\s+build\b/i,
/\bbuild\s+(?:error|fail|broken|fix)/i,
/\btsc\s+error/i,
/\bcompile\s+error/i,
/\bci\s+(?:fail|broken|fix)/i
]
},
{
intent: "debug",
patterns: [
/\bdebug(?:ging)?\b/i,
/\btroubleshoot(?:ing)?\b/i,
/\binvestigate\b/i,
/\broot.?cause\b/i,
/\bwhy\s+(?:is|does|did|are)\b/i,
/\bdiagnos(?:e|ing)\b/i,
/\btrace\s+(?:the|an?)\s+(?:bug|issue|error|problem)/i
]
},
{
intent: "docs",
patterns: [
/\bdocument(?:ation|ing|ation)?\b/i,
/\bwrite\s+(?:docs|readme|changelog|comments|jsdoc|tsdoc)/i,
/\bupdate\s+(?:docs|readme|changelog)/i,
/\badd\s+(?:docs|comments|jsdoc|tsdoc)\b/i,
/\breadme\b/i,
/\bchangelog\b/i
]
},
{
intent: "design",
patterns: [
/\bdesign\b/i,
/\barchitect(?:ure|ing)?\b/i,
/\bui\s+(?:design|layout|component)/i,
/\bux\b/i,
/\bwireframe\b/i,
/\bmockup\b/i,
/\bprototype\b/i,
/\bsystem\s+design\b/i,
/\bapi\s+design\b/i
]
},
{
intent: "cleanup",
patterns: [
/\bclean\s*up\b/i,
/\brefactor(?:ing)?\b/i,
/\bsimplif(?:y|ying)\b/i,
/\bdead\s+code\b/i,
/\bunused\s+(?:code|import|variable|function)\b/i,
/\bremove\s+(?:dead|unused|legacy)\b/i,
/\bdebt\b/i
]
},
{
intent: "review",
patterns: [
/\breview\b/i,
/\baudit\b/i,
/\bpr\s+review\b/i,
/\bcode\s+review\b/i,
/\bcheck\s+(?:the\s+)?(?:code|pr|pull.?request)\b/i
]
},
{
intent: "verification",
patterns: [
/\btest(?:ing|s)?\b/i,
/\bverif(?:y|ication)\b/i,
/\bvalidat(?:e|ion)\b/i,
/\bunit\s+test\b/i,
/\bintegration\s+test\b/i,
/\be2e\b/i,
/\bspec\b/i,
/\bcoverage\b/i,
/\bassert(?:ion)?\b/i
]
},
{
intent: "implementation",
patterns: [
/\bimplement(?:ing|ation)?\b/i,
/\badd\s+(?:the\s+)?(?:feature|function|method|class|endpoint|route)\b/i,
/\bbuild\s+(?:the\s+)?(?:feature|component|module|service|api)\b/i,
/\bcreate\s+(?:the\s+)?(?:feature|component|module|service|api|function)\b/i,
/\bwrite\s+(?:the\s+)?(?:code|function|class|method|module)\b/i
]
}
];
var SECURITY_DOMAIN_RE = /\b(?:auth(?:entication|orization)?|cve|injection|owasp|security|vulnerability|vuln|xss|csrf|sqli|rce|privilege.?escalat)\b/i;
var ROLE_KEYWORDS = {
"build-fixer": [/\bbuild\b/i, /\bci\b/i, /\bcompile\b/i, /\btsc\b/i, /\blint\b/i],
debugger: [/\bdebug\b/i, /\btroubleshoot\b/i, /\binvestigate\b/i, /\bdiagnos/i],
writer: [/\bdoc(?:ument)?/i, /\breadme\b/i, /\bchangelog\b/i, /\bcomment/i],
designer: [/\bdesign\b/i, /\barchitect/i, /\bui\b/i, /\bux\b/i, /\bwireframe\b/i],
"code-simplifier": [/\brefactor/i, /\bclean/i, /\bsimplif/i, /\bdebt\b/i, /\bunused\b/i],
"security-reviewer": [/\bsecurity\b/i, /\bvulnerabilit/i, /\bcve\b/i, /\bowasp\b/i, /\bxss\b/i],
"quality-reviewer": [/\breview\b/i, /\baudit\b/i, /\bcheck\b/i],
"test-engineer": [/\btest/i, /\bverif/i, /\bvalidat/i, /\bspec\b/i, /\bcoverage\b/i],
executor: [/\bimplement/i, /\bbuild\b/i, /\bcreate\b/i, /\badd\b/i, /\bwrite\b/i]
};
function inferLaneIntent(text) {
if (!text || text.trim().length === 0) return "unknown";
for (const { intent, patterns } of INTENT_PATTERNS) {
for (const pattern of patterns) {
if (pattern.test(text)) {
return intent;
}
}
}
return "unknown";
}
function routeTaskToRole(taskSubject, taskDescription, fallbackRole) {
const combined = `${taskSubject} ${taskDescription}`.trim();
const intent = inferLaneIntent(combined);
const isSecurityDomain = SECURITY_DOMAIN_RE.test(combined);
switch (intent) {
case "build-fix":
return { role: "build-fixer", confidence: "high", reason: "build-fix intent detected" };
case "debug":
return { role: "debugger", confidence: "high", reason: "debug intent detected" };
case "docs":
return { role: "writer", confidence: "high", reason: "docs intent detected" };
case "design":
return { role: "designer", confidence: "high", reason: "design intent detected" };
case "cleanup":
return { role: "code-simplifier", confidence: "high", reason: "cleanup intent detected" };
case "review":
if (isSecurityDomain) {
return { role: "security-reviewer", confidence: "high", reason: "review intent with security domain detected" };
}
return { role: "quality-reviewer", confidence: "high", reason: "review intent detected" };
case "verification":
return { role: "test-engineer", confidence: "high", reason: "verification intent detected" };
case "implementation":
return {
role: fallbackRole,
confidence: "medium",
reason: isSecurityDomain ? "implementation intent with security domain \u2014 stays on fallback role" : "implementation intent \u2014 using fallback role"
};
case "unknown":
default: {
const best = scoreByKeywords(combined);
if (best) {
return {
role: best.role,
confidence: "medium",
reason: `keyword match (${best.count} hits) for role '${best.role}'`
};
}
return {
role: fallbackRole,
confidence: "low",
reason: "no clear intent signal \u2014 using fallback role"
};
}
}
}
function scoreByKeywords(text) {
let bestRole = null;
let bestCount = 0;
for (const [role, patterns] of Object.entries(ROLE_KEYWORDS)) {
const count = patterns.filter((p) => p.test(text)).length;
if (count > bestCount) {
bestCount = count;
bestRole = role;
}
}
return bestRole && bestCount > 0 ? { role: bestRole, count: bestCount } : null;
}
// src/team/cli-worker-contract.ts
var CONTRACT_ROLES = /* @__PURE__ */ new Set([
"critic",
"code-reviewer",
"security-reviewer",
"test-engineer"
]);
var VALID_VERDICTS = /* @__PURE__ */ new Set(["approve", "revise", "reject"]);
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "major", "minor", "nit"]);
function shouldInjectContract(role, provider) {
if (!role || !provider) return false;
if (provider === "claude" || provider === "cursor") return false;
return CONTRACT_ROLES.has(role);
}
function renderCliWorkerOutputContract(role, output_file) {
return [
"",
"---",
"## REQUIRED: Structured Verdict Output",
"",
`You are acting in the \`${role}\` role. Before you exit, write a JSON verdict to:`,
"",
` ${output_file}`,
"",
"Schema (all keys required; `findings` may be an empty array):",
"",
"```json",
"{",
` "role": "${role}",`,
' "task_id": "<task id from the assignment above>",',
' "verdict": "approve" | "revise" | "reject",',
' "summary": "one- or two-sentence overall assessment",',
' "findings": [',
" {",
' "severity": "critical" | "major" | "minor" | "nit",',
' "message": "what is wrong and why it matters",',
' "file": "optional/path/to/file",',
' "line": 42',
" }",
" ]",
"}",
"```",
"",
"Rules:",
"- Write valid JSON only (no surrounding prose, no markdown fences in the file).",
"- `verdict` MUST be one of `approve`, `revise`, or `reject`.",
"- Each finding MUST carry a `severity` from the enum above.",
"- Use `approve` only when you have no blocking concerns.",
'- If you cannot produce a verdict, write `{"verdict":"revise", ...}` with an explanatory finding rather than exiting silently.',
"- The team leader reads this file to mark the task complete; omitting it leaves the task stuck in_progress pending human review.",
""
].join("\n");
}
function parseCliWorkerVerdict(raw) {
let parsed;
try {
parsed = JSON.parse(raw);
} catch (err) {
throw new Error(`verdict_json_parse_failed: ${err.message}`);
}
if (!parsed || typeof parsed !== "object") {
throw new Error("verdict_not_object");
}
const obj = parsed;
const role = obj.role;
if (typeof role !== "string" || !role) {
throw new Error("verdict_missing_role");
}
const taskId = obj.task_id;
if (typeof taskId !== "string" || !taskId) {
throw new Error("verdict_missing_task_id");
}
const verdict = obj.verdict;
if (typeof verdict !== "string" || !VALID_VERDICTS.has(verdict)) {
throw new Error(`verdict_invalid_verdict:${String(verdict)}`);
}
const summary = obj.summary;
if (typeof summary !== "string") {
throw new Error("verdict_missing_summary");
}
const findingsRaw = obj.findings;
if (!Array.isArray(findingsRaw)) {
throw new Error("verdict_findings_not_array");
}
const findings = findingsRaw.map((entry, idx) => {
if (!entry || typeof entry !== "object") {
throw new Error(`verdict_finding_${idx}_not_object`);
}
const f = entry;
const severity = f.severity;
if (typeof severity !== "string" || !VALID_SEVERITIES.has(severity)) {
throw new Error(`verdict_finding_${idx}_invalid_severity:${String(severity)}`);
}
const message = f.message;
if (typeof message !== "string" || !message) {
throw new Error(`verdict_finding_${idx}_missing_message`);
}
const finding = {
severity,
message
};
if (typeof f.file === "string" && f.file) finding.file = f.file;
if (typeof f.line === "number" && Number.isFinite(f.line)) finding.line = f.line;
return finding;
});
return {
role,
task_id: taskId,
verdict,
summary,
findings
};
}
function cliWorkerOutputFilePath(teamStateRootAbs, workerName2) {
return `${teamStateRootAbs.replaceAll("\\", "/")}/workers/${workerName2}/verdict.json`;
}
// src/team/runtime-v2.ts
function isRuntimeV2Enabled(env = process.env) {
const raw = env.OMC_RUNTIME_V2;
if (!raw) return true;
const normalized = raw.trim().toLowerCase();
return !["0", "false", "no", "off"].includes(normalized);
}
var MONITOR_SIGNAL_STALE_MS = 3e4;
function resolveTaskAssignment(task, resolvedRouting, roleRoutingConfig, resolvedBinaryPaths, fallbackAgent) {
const canonicalRoles = new Set(CANONICAL_TEAM_ROLES);
const hasExplicitRole = typeof task.role === "string" && task.role.length > 0;
const rawRole = hasExplicitRole ? task.role : routeTaskToRole(task.subject, task.description, "executor").role;
const normalized = normalizeDelegationRole(rawRole);
const canonical = canonicalRoles.has(normalized) ? normalized : null;
if (!canonical) {
return { agentType: fallbackAgent, model: "", role: null };
}
const hasConfigForRole = !!getRoleRoutingSpec(
roleRoutingConfig,
canonical
);
if (!hasExplicitRole && !hasConfigForRole) {
return { agentType: fallbackAgent, model: "", role: canonical };
}
const pair = resolvedRouting[canonical];
if (!pair) {
return { agentType: fallbackAgent, model: "", role: canonical };
}
const primaryProvider = pair.primary.provider;
const chosen = resolvedBinaryPaths[primaryProvider] ? pair.primary : pair.fallback;
return {
agentType: chosen.provider,
model: chosen.model,
role: canonical
};
}
function sanitizeTeamName(name) {
const sanitized = name.toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 30);
if (!sanitized) throw new Error(`Invalid team name: "${name}" produces empty slug after sanitization`);
return sanitized;
}
function shouldUseLaunchTimeCliResolution(reason) {
return /untrusted location|relative path/i.test(reason);
}
function resolvePreflightBinaryPath(agentType) {
try {
return { path: resolveValidatedBinaryPath(agentType), degraded: false };
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
if (shouldUseLaunchTimeCliResolution(reason)) {
return { path: getContract(agentType).binary, degraded: true, reason };
}
throw err;
}
}
async function isWorkerPaneAlive(paneId) {
if (!paneId) return false;
try {
const { isWorkerAlive: isWorkerAlive2 } = await Promise.resolve().then(() => (init_tmux_session(), tmux_session_exports));
return await isWorkerAlive2(paneId);
} catch {
return false;
}
}
async function captureWorkerPane(paneId) {
if (!paneId) return "";
try {
const result = await tmuxExecAsync(["capture-pane", "-t", paneId, "-p", "-S", "-80"]);
return result.stdout ?? "";
} catch {
return "";
}
}
function isFreshTimestamp(value, maxAgeMs = MONITOR_SIGNAL_STALE_MS) {
if (!value) return false;
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) return false;
return Date.now() - parsed <= maxAgeMs;
}
function findOutstandingWorkerTask(worker, taskById, inProgressByOwner) {
if (typeof worker.assigned_tasks === "object") {
for (const taskId of worker.assigned_tasks) {
const task = taskById.get(taskId);
if (task && (task.status === "pending" || task.status === "in_progress")) {
return task;
}
}
}
const owned = inProgressByOwner.get(worker.name) ?? [];
return owned[0] ?? null;
}
function getTaskDependencyIds(task) {
return task.depends_on ?? task.blocked_by ?? [];
}
function getMissingDependencyIds(task, taskById) {
return getTaskDependencyIds(task).filter((dependencyId) => !taskById.has(dependencyId));
}
function buildV2TaskInstruction(teamName, workerName2, task, taskId, cliOutputContract) {
const claimTaskCommand = formatOmcCliInvocation(
`team api claim-task --input '${JSON.stringify({ team_name: teamName, task_id: taskId, worker: workerName2 })}' --json`,
{}
);
const completeTaskCommand = formatOmcCliInvocation(
`team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>" })}' --json`
);
const failTaskCommand = formatOmcCliInvocation(
`team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "failed", claim_token: "<claim_token>" })}' --json`
);
return [
`## REQUIRED: Task Lifecycle Commands`,
`You MUST run these commands. Do NOT skip any step.`,
``,
`1. Claim your task:`,
` ${claimTaskCommand}`,
` Save the claim_token from the response.`,
`2. Do the work described below.`,
`3. On completion (use claim_token from step 1):`,
` ${completeTaskCommand}`,
`4. On failure (use claim_token from step 1):`,
` ${failTaskCommand}`,
`5. ACK/progress replies are not a stop signal. Keep executing your assigned or next feasible work until the task is actually complete or failed, then transition and exit.`,
``,
`## Task Assignment`,
`Task ID: ${taskId}`,
`Worker: ${workerName2}`,
`Subject: ${task.subject}`,
``,
task.description,
``,
`REMINDER: You MUST run transition-task-status before exiting. Do NOT write done.json or edit task files directly.`,
...cliOutputContract ? [cliOutputContract] : []
].join("\n");
}
async function notifyStartupInbox(sessionName2, paneId, message) {
const notified = await notifyPaneWithRetry2(sessionName2, paneId, message);
return notified ? { ok: true, transport: "tmux_send_keys", reason: "worker_pane_notified" } : { ok: false, transport: "tmux_send_keys", reason: "worker_notify_failed" };
}
async function notifyPaneWithRetry2(sessionName2, paneId, message, maxAttempts = 6, retryDelayMs = 350) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
if (await sendToWorker(sessionName2, paneId, message)) {
return true;
}
if (attempt < maxAttempts) {
await new Promise((r) => setTimeout(r, retryDelayMs));
}
}
return false;
}
function hasWorkerStatusProgress(status, taskId) {
if (status.current_task_id === taskId) return true;
return ["working", "blocked", "done", "failed"].includes(status.state);
}
async function hasWorkerTaskClaimEvidence(teamName, workerName2, cwd, taskId) {
try {
const raw = await (0, import_promises7.readFile)(absPath(cwd, TeamPaths.taskFile(teamName, taskId)), "utf-8");
const task = JSON.parse(raw);
return task.owner === workerName2 && ["in_progress", "completed", "failed"].includes(task.status);
} catch {
return false;
}
}
async function hasWorkerStartupEvidence(teamName, workerName2, taskId, cwd) {
const [hasClaimEvidence, status] = await Promise.all([
hasWorkerTaskClaimEvidence(teamName, workerName2, cwd, taskId),
readWorkerStatus(teamName, workerName2, cwd)
]);
return hasClaimEvidence || hasWorkerStatusProgress(status, taskId);
}
async function waitForWorkerStartupEvidence(teamName, workerName2, taskId, cwd, attempts = 3, delayMs = 250) {
for (let attempt = 1; attempt <= attempts; attempt++) {
if (await hasWorkerStartupEvidence(teamName, workerName2, taskId, cwd)) {
return true;
}
if (attempt < attempts) {
await new Promise((resolve5) => setTimeout(resolve5, delayMs));
}
}
return false;
}
async function spawnV2Worker(opts) {
const splitTarget = opts.existingWorkerPaneIds.length === 0 ? opts.leaderPaneId : opts.existingWorkerPaneIds[opts.existingWorkerPaneIds.length - 1];
const splitType = opts.existingWorkerPaneIds.length === 0 ? "-h" : "-v";
const splitResult = await tmuxExecAsync([
"split-window",
splitType,
"-t",
splitTarget,
"-d",
"-P",
"-F",
"#{pane_id}",
"-c",
opts.cwd
]);
const paneId = splitResult.stdout.split("\n")[0]?.trim();
if (!paneId) {
return { paneId: null, startupAssigned: false, startupFailureReason: "pane_id_missing" };
}
const usePromptMode = isPromptModeAgent(opts.agentType);
const injectContract = shouldInjectContract(opts.role ?? null, opts.agentType);
const outputFile = injectContract && opts.role ? cliWorkerOutputFilePath(teamStateRoot(opts.cwd, opts.teamName), opts.workerName) : void 0;
const cliOutputContract = injectContract && opts.role && outputFile ? renderCliWorkerOutputContract(opts.role, outputFile) : void 0;
const instruction = buildV2TaskInstruction(
opts.teamName,
opts.workerName,
opts.task,
opts.taskId,
cliOutputContract
);
const inboxTriggerMessage = generateTriggerMessage(opts.teamName, opts.workerName);
const promptModeStartupPrompt = generatePromptModeStartupPrompt(
opts.teamName,
opts.workerName,
void 0,
cliOutputContract
);
if (usePromptMode) {
await composeInitialInbox(
opts.teamName,
opts.workerName,
instruction,
opts.cwd,
cliOutputContract
);
}
const envVars = {
...getWorkerEnv(opts.teamName, opts.workerName, opts.agentType),
OMC_TEAM_STATE_ROOT: teamStateRoot(opts.cwd, opts.teamName),
OMC_TEAM_LEADER_CWD: opts.cwd
};
const resolvedBinaryPath = opts.resolvedBinaryPaths[opts.agentType] ?? resolveValidatedBinaryPath(opts.agentType);
const modelForAgent = opts.model ?? (() => {
if (opts.agentType === "codex") {
return process.env.OMC_EXTERNAL_MODELS_DEFAULT_CODEX_MODEL || process.env.OMC_CODEX_DEFAULT_MODEL || void 0;
}
if (opts.agentType === "gemini") {
return process.env.OMC_EXTERNAL_MODELS_DEFAULT_GEMINI_MODEL || process.env.OMC_GEMINI_DEFAULT_MODEL || void 0;
}
return resolveClaudeWorkerModel();
})();
const [launchBinary, ...launchArgs] = buildWorkerArgv(opts.agentType, {
teamName: opts.teamName,
workerName: opts.workerName,
cwd: opts.cwd,
resolvedBinaryPath,
model: modelForAgent
});
if (usePromptMode) {
launchArgs.push(...getPromptModeArgs(opts.agentType, promptModeStartupPrompt));
}
const paneConfig = {
teamName: opts.teamName,
workerName: opts.workerName,
envVars,
launchBinary,
launchArgs,
cwd: opts.cwd
};
await spawnWorkerInPane(opts.sessionName, paneId, paneConfig);
await applyMainVerticalLayout(opts.sessionName);
if (!usePromptMode) {
const paneReady = await waitForPaneReady(paneId);
if (!paneReady) {
return {
paneId,
startupAssigned: false,
startupFailureReason: "worker_pane_not_ready"
};
}
}
const dispatchOutcome = await queueInboxInstruction({
teamName: opts.teamName,
workerName: opts.workerName,
workerIndex: opts.workerIndex + 1,
paneId,
inbox: instruction,
triggerMessage: inboxTriggerMessage,
cwd: opts.cwd,
transportPreference: usePromptMode ? "prompt_stdin" : "transport_direct",
fallbackAllowed: false,
inboxCorrelationKey: `startup:${opts.workerName}:${opts.taskId}`,
notify: async (_target, triggerMessage) => {
if (usePromptMode) {
return { ok: true, transport: "prompt_stdin", reason: "prompt_mode_launch_args" };
}
if (opts.agentType === "gemini") {
const confirmed = await notifyPaneWithRetry2(opts.sessionName, paneId, "1");
if (!confirmed) {
return { ok: false, transport: "tmux_send_keys", reason: "worker_notify_failed:trust-confirm" };
}
await new Promise((r) => setTimeout(r, 800));
}
return notifyStartupInbox(opts.sessionName, paneId, triggerMessage);
},
deps: {
writeWorkerInbox
}
});
if (!dispatchOutcome.ok) {
return {
paneId,
startupAssigned: false,
startupFailureReason: dispatchOutcome.reason
};
}
if (opts.agentType === "claude") {
const settled = await waitForWorkerStartupEvidence(
opts.teamName,
opts.workerName,
opts.taskId,
opts.cwd,
6
);
if (!settled) {
return {
paneId,
startupAssigned: false,
startupFailureReason: "claude_startup_evidence_missing"
};
}
}
if (usePromptMode) {
const settled = await waitForWorkerStartupEvidence(
opts.teamName,
opts.workerName,
opts.taskId,
opts.cwd
);
if (!settled) {
return {
paneId,
startupAssigned: false,
startupFailureReason: `${opts.agentType}_startup_evidence_missing`
};
}
}
return {
paneId,
startupAssigned: true,
...outputFile ? { outputFile } : {}
};
}
async function startTeamV2(config) {
const sanitized = sanitizeTeamName(config.teamName);
const leaderCwd = (0, import_path19.resolve)(config.cwd);
validateTeamName(sanitized);
const pluginCfg = config.pluginConfig ?? loadConfig();
const resolvedRouting = buildResolvedRoutingSnapshot(pluginCfg);
const agentTypes = config.agentTypes;
const resolvedBinaryPaths = {};
const missingBinaryReasons = [];
for (const agentType of [...new Set(agentTypes)]) {
try {
resolvedBinaryPaths[agentType] = resolvePreflightBinaryPath(agentType).path;
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
missingBinaryReasons.push({ agentType, reason });
}
}
for (const { primary } of Object.values(resolvedRouting)) {
const provider = primary.provider;
if (resolvedBinaryPaths[provider]) continue;
if (missingBinaryReasons.some((m) => m.agentType === provider)) continue;
try {
resolvedBinaryPaths[provider] = resolvePreflightBinaryPath(provider).path;
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
missingBinaryReasons.push({ agentType: provider, reason });
}
}
if (!resolvedBinaryPaths.claude) {
try {
resolvedBinaryPaths.claude = resolveValidatedBinaryPath("claude");
} catch {
}
}
await (0, import_promises7.mkdir)(absPath(leaderCwd, TeamPaths.tasks(sanitized)), { recursive: true });
await (0, import_promises7.mkdir)(absPath(leaderCwd, TeamPaths.workers(sanitized)), { recursive: true });
await (0, import_promises7.mkdir)((0, import_path19.join)(leaderCwd, ".omc", "state", "team", sanitized, "mailbox"), { recursive: true });
const missingBinaryLogFailure = createSwallowedErrorLogger(
"team.runtime-v2.startTeamV2 cli_binary_missing event failed"
);
for (const { agentType, reason } of missingBinaryReasons) {
process.stderr.write(
`[team/runtime-v2] cli_binary_missing:${agentType}: ${reason} \u2014 falling back to claude snapshot (AC-8)
`
);
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `cli_binary_missing:${agentType}:${reason}`
}, leaderCwd).catch(missingBinaryLogFailure);
}
for (let i = 0; i < config.tasks.length; i++) {
const taskId = String(i + 1);
const taskFilePath = absPath(leaderCwd, TeamPaths.taskFile(sanitized, taskId));
await (0, import_promises7.mkdir)((0, import_path19.join)(taskFilePath, ".."), { recursive: true });
await (0, import_promises7.writeFile)(taskFilePath, JSON.stringify({
id: taskId,
subject: config.tasks[i].subject,
description: config.tasks[i].description,
status: "pending",
owner: null,
result: null,
created_at: (/* @__PURE__ */ new Date()).toISOString()
}, null, 2), "utf-8");
}
const workerNames = Array.from({ length: config.workerCount }, (_, index) => `worker-${index + 1}`);
const workerNameSet = new Set(workerNames);
const startupAllocations = [];
const unownedTaskIndices = [];
for (let i = 0; i < config.tasks.length; i++) {
const owner = config.tasks[i]?.owner;
if (typeof owner === "string" && workerNameSet.has(owner)) {
startupAllocations.push({ workerName: owner, taskIndex: i });
} else {
unownedTaskIndices.push(i);
}
}
if (unownedTaskIndices.length > 0) {
const allocationTasks = unownedTaskIndices.map((idx) => ({
id: String(idx),
subject: config.tasks[idx].subject,
description: config.tasks[idx].description
}));
const allocationWorkers = workerNames.map((name, i) => ({
name,
role: config.workerRoles?.[i] ?? (agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude"),
currentLoad: 0
}));
for (const r of allocateTasksToWorkers(allocationTasks, allocationWorkers)) {
startupAllocations.push({ workerName: r.workerName, taskIndex: Number(r.taskId) });
}
}
for (let i = 0; i < workerNames.length; i++) {
const wName = workerNames[i];
const agentType = agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude";
await ensureWorkerStateDir(sanitized, wName, leaderCwd);
await writeWorkerOverlay({
teamName: sanitized,
workerName: wName,
agentType,
tasks: config.tasks.map((t, idx) => ({
id: String(idx + 1),
subject: t.subject,
description: t.description
})),
cwd: leaderCwd,
...config.rolePrompt ? { bootstrapInstructions: config.rolePrompt } : {}
});
}
const session = await createTeamSession(sanitized, 0, leaderCwd, {
newWindow: Boolean(config.newWindow)
});
const sessionName2 = session.sessionName;
const leaderPaneId = session.leaderPaneId;
const ownsWindow = session.sessionMode !== "split-pane";
const workerPaneIds = [];
const workersInfo = workerNames.map((wName, i) => ({
name: wName,
index: i + 1,
role: config.workerRoles?.[i] ?? (agentTypes[i % agentTypes.length] ?? agentTypes[0] ?? "claude"),
assigned_tasks: [],
working_dir: leaderCwd
}));
const teamConfig = {
name: sanitized,
task: config.tasks.map((t) => t.subject).join("; "),
agent_type: agentTypes[0] || "claude",
worker_launch_mode: "interactive",
policy: DEFAULT_TEAM_TRANSPORT_POLICY,
governance: DEFAULT_TEAM_GOVERNANCE,
worker_count: config.workerCount,
max_workers: 20,
workers: workersInfo,
created_at: (/* @__PURE__ */ new Date()).toISOString(),
tmux_session: sessionName2,
tmux_window_owned: ownsWindow,
next_task_id: config.tasks.length + 1,
leader_cwd: leaderCwd,
team_state_root: teamStateRoot(leaderCwd, sanitized),
leader_pane_id: leaderPaneId,
hud_pane_id: null,
resize_hook_name: null,
resize_hook_target: null,
resolved_routing: resolvedRouting,
...ownsWindow ? { workspace_mode: "single" } : {}
};
await saveTeamConfig(teamConfig, leaderCwd);
const permissionsSnapshot = {
approval_mode: process.env.OMC_APPROVAL_MODE || "default",
sandbox_mode: process.env.OMC_SANDBOX_MODE || "default",
network_access: process.env.OMC_NETWORK_ACCESS === "1"
};
const teamManifest = {
schema_version: 2,
name: sanitized,
task: teamConfig.task,
leader: {
session_id: sessionName2,
worker_id: "leader-fixed",
role: "leader"
},
policy: DEFAULT_TEAM_TRANSPORT_POLICY,
governance: DEFAULT_TEAM_GOVERNANCE,
permissions_snapshot: permissionsSnapshot,
tmux_session: sessionName2,
worker_count: teamConfig.worker_count,
workers: workersInfo,
next_task_id: teamConfig.next_task_id,
created_at: teamConfig.created_at,
leader_cwd: leaderCwd,
team_state_root: teamConfig.team_state_root,
workspace_mode: teamConfig.workspace_mode,
leader_pane_id: leaderPaneId,
hud_pane_id: null,
resize_hook_name: null,
resize_hook_target: null,
next_worker_index: teamConfig.next_worker_index
};
await (0, import_promises7.writeFile)(absPath(leaderCwd, TeamPaths.manifest(sanitized)), JSON.stringify(teamManifest, null, 2), "utf-8");
const initialStartupAllocations = [];
const seenStartupWorkers = /* @__PURE__ */ new Set();
for (const decision of startupAllocations) {
if (seenStartupWorkers.has(decision.workerName)) continue;
initialStartupAllocations.push(decision);
seenStartupWorkers.add(decision.workerName);
if (initialStartupAllocations.length >= config.workerCount) break;
}
for (const decision of initialStartupAllocations) {
const wName = decision.workerName;
const workerIndex = Number.parseInt(wName.replace("worker-", ""), 10) - 1;
const taskId = String(decision.taskIndex + 1);
const task = config.tasks[decision.taskIndex];
if (!task || workerIndex < 0) continue;
const fallbackAgent = agentTypes[workerIndex % agentTypes.length] ?? agentTypes[0] ?? "claude";
const assignment = resolveTaskAssignment(
task,
resolvedRouting,
pluginCfg.team?.roleRouting,
resolvedBinaryPaths,
fallbackAgent
);
const workerLaunch = await spawnV2Worker({
sessionName: sessionName2,
leaderPaneId,
existingWorkerPaneIds: workerPaneIds,
teamName: sanitized,
workerName: wName,
workerIndex,
agentType: assignment.agentType,
task,
taskId,
cwd: leaderCwd,
resolvedBinaryPaths,
...assignment.model ? { model: assignment.model } : {},
...assignment.role ? { role: assignment.role } : {}
});
if (workerLaunch.paneId) {
workerPaneIds.push(workerLaunch.paneId);
const workerInfo = workersInfo[workerIndex];
if (workerInfo) {
workerInfo.pane_id = workerLaunch.paneId;
workerInfo.assigned_tasks = workerLaunch.startupAssigned ? [taskId] : [];
workerInfo.worker_cli = assignment.agentType;
if (workerLaunch.outputFile) {
workerInfo.output_file = workerLaunch.outputFile;
}
}
}
if (workerLaunch.startupFailureReason) {
const logEventFailure2 = createSwallowedErrorLogger(
"team.runtime-v2.startTeamV2 appendTeamEvent failed"
);
appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `startup_manual_intervention_required:${wName}:${workerLaunch.startupFailureReason}`
}, leaderCwd).catch(logEventFailure2);
}
}
teamConfig.workers = workersInfo;
await saveTeamConfig(teamConfig, leaderCwd);
const logEventFailure = createSwallowedErrorLogger(
"team.runtime-v2.startTeamV2 appendTeamEvent failed"
);
appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `start_team_v2: workers=${config.workerCount} tasks=${config.tasks.length} panes=${workerPaneIds.length}`
}, leaderCwd).catch(logEventFailure);
return {
teamName: sanitized,
sanitizedName: sanitized,
sessionName: sessionName2,
config: teamConfig,
cwd: leaderCwd,
ownsWindow
};
}
async function processCliWorkerVerdicts(teamName, cwd) {
const sanitized = sanitizeTeamName(teamName);
const config = await readTeamConfig(sanitized, cwd);
if (!config) return [];
const results = [];
const logEventFailure = createSwallowedErrorLogger(
"team.runtime-v2.processCliWorkerVerdicts appendTeamEvent failed"
);
const { rename: rename4 } = await import("fs/promises");
const { readFileSync: readFileSync11, writeFileSync: writeFileSync2, existsSync: fsExistsSync } = await import("fs");
const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
for (const worker of config.workers) {
const outputFile = worker.output_file;
if (!outputFile) continue;
const alive = await isWorkerPaneAlive(worker.pane_id);
if (alive) continue;
if (!fsExistsSync(outputFile)) {
results.push({ workerName: worker.name, taskId: null, status: "file_missing" });
continue;
}
let payload;
try {
const raw = await (0, import_promises7.readFile)(outputFile, "utf-8");
payload = parseCliWorkerVerdict(raw);
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `cli_worker_verdict_parse_failed:${worker.name}:${reason}`
}, cwd).catch(logEventFailure);
results.push({ workerName: worker.name, taskId: null, status: "parse_failed", reason });
continue;
}
const candidateTaskIds = /* @__PURE__ */ new Set();
if (payload.task_id) candidateTaskIds.add(payload.task_id);
for (const id of worker.assigned_tasks ?? []) candidateTaskIds.add(id);
let targetTaskId = null;
let targetTaskPath = null;
for (const taskId of candidateTaskIds) {
const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, taskId));
if (!fsExistsSync(taskPath2)) continue;
try {
const taskRaw = readFileSync11(taskPath2, "utf-8");
const taskData = JSON.parse(taskRaw);
if (taskData.owner === worker.name && taskData.status === "in_progress") {
targetTaskId = taskId;
targetTaskPath = taskPath2;
break;
}
} catch {
}
}
if (!targetTaskId || !targetTaskPath) {
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `cli_worker_verdict_no_in_progress_task:${worker.name}:verdict=${payload.verdict}`
}, cwd).catch(logEventFailure);
results.push({
workerName: worker.name,
taskId: payload.task_id,
status: "no_in_progress_task",
verdict: payload.verdict
});
continue;
}
const terminalStatus = payload.verdict === "approve" ? "completed" : "failed";
let transitionOk = false;
try {
withFileLockSync2(targetTaskPath + ".lock", () => {
const raw = readFileSync11(targetTaskPath, "utf-8");
const taskData = JSON.parse(raw);
if (taskData.status !== "in_progress" || taskData.owner !== worker.name) {
return;
}
const prevMetadata = taskData.metadata && typeof taskData.metadata === "object" ? taskData.metadata : {};
taskData.status = terminalStatus;
taskData.completed_at = (/* @__PURE__ */ new Date()).toISOString();
taskData.claim = void 0;
taskData.metadata = {
...prevMetadata,
verdict: payload.verdict,
verdict_summary: payload.summary,
verdict_findings: payload.findings,
verdict_role: payload.role,
verdict_source: "cli_worker_output_contract"
};
if (terminalStatus === "failed") {
taskData.error = `cli_worker_verdict:${payload.verdict}:${payload.summary}`;
}
writeFileSync2(targetTaskPath, JSON.stringify(taskData, null, 2), "utf-8");
transitionOk = true;
});
} catch {
}
if (!transitionOk) {
results.push({
workerName: worker.name,
taskId: targetTaskId,
status: "already_terminal",
verdict: payload.verdict
});
continue;
}
await appendTeamEvent(sanitized, {
type: terminalStatus === "completed" ? "task_completed" : "task_failed",
worker: worker.name,
task_id: targetTaskId,
reason: `cli_worker_verdict:${payload.verdict}`
}, cwd).catch(logEventFailure);
try {
await rename4(outputFile, outputFile + ".processed");
} catch {
}
results.push({
workerName: worker.name,
taskId: targetTaskId,
status: terminalStatus,
verdict: payload.verdict
});
}
return results;
}
async function monitorTeamV2(teamName, cwd) {
const monitorStartMs = import_perf_hooks.performance.now();
const sanitized = sanitizeTeamName(teamName);
const config = await readTeamConfig(sanitized, cwd);
if (!config) return null;
try {
await processCliWorkerVerdicts(sanitized, cwd);
} catch (err) {
process.stderr.write(
`[team/runtime-v2] processCliWorkerVerdicts failed: ${err instanceof Error ? err.message : String(err)}
`
);
}
const previousSnapshot = await readMonitorSnapshot(sanitized, cwd);
const listTasksStartMs = import_perf_hooks.performance.now();
const allTasks = await listTasksFromFiles(sanitized, cwd);
const listTasksMs = import_perf_hooks.performance.now() - listTasksStartMs;
const taskById = new Map(allTasks.map((task) => [task.id, task]));
const inProgressByOwner = /* @__PURE__ */ new Map();
for (const task of allTasks) {
if (task.status !== "in_progress" || !task.owner) continue;
const existing = inProgressByOwner.get(task.owner) || [];
existing.push(task);
inProgressByOwner.set(task.owner, existing);
}
const workers = [];
const deadWorkers = [];
const nonReportingWorkers = [];
const recommendations = [];
const workerScanStartMs = import_perf_hooks.performance.now();
const workerSignals = await Promise.all(
config.workers.map(async (worker) => {
const alive = await isWorkerPaneAlive(worker.pane_id);
const [status, heartbeat, paneCapture] = await Promise.all([
readWorkerStatus(sanitized, worker.name, cwd),
readWorkerHeartbeat(sanitized, worker.name, cwd),
alive ? captureWorkerPane(worker.pane_id) : Promise.resolve("")
]);
return { worker, alive, status, heartbeat, paneCapture };
})
);
const workerScanMs = import_perf_hooks.performance.now() - workerScanStartMs;
for (const { worker: w, alive, status, heartbeat, paneCapture } of workerSignals) {
const currentTask = status.current_task_id ? taskById.get(status.current_task_id) ?? null : null;
const outstandingTask = currentTask ?? findOutstandingWorkerTask(w, taskById, inProgressByOwner);
const expectedTaskId = status.current_task_id ?? outstandingTask?.id ?? w.assigned_tasks[0] ?? "";
const previousTurns = previousSnapshot ? previousSnapshot.workerTurnCountByName[w.name] ?? 0 : null;
const previousTaskId = previousSnapshot?.workerTaskIdByName[w.name] ?? "";
const currentTaskId = status.current_task_id ?? "";
const turnsWithoutProgress = heartbeat && previousTurns !== null && status.state === "working" && currentTask && (currentTask.status === "pending" || currentTask.status === "in_progress") && currentTaskId !== "" && previousTaskId === currentTaskId ? Math.max(0, heartbeat.turn_count - previousTurns) : 0;
workers.push({
name: w.name,
alive,
status,
heartbeat,
assignedTasks: w.assigned_tasks,
turnsWithoutProgress
});
if (!alive) {
deadWorkers.push(w.name);
const deadWorkerTasks = inProgressByOwner.get(w.name) || [];
for (const t of deadWorkerTasks) {
recommendations.push(`Reassign task-${t.id} from dead ${w.name}`);
}
}
const paneSuggestsIdle = alive && paneLooksReady(paneCapture) && !paneHasActiveTask(paneCapture);
const statusFresh = isFreshTimestamp(status.updated_at);
const heartbeatFresh = isFreshTimestamp(heartbeat?.last_turn_at);
const hasWorkStartEvidence = expectedTaskId !== "" && hasWorkerStatusProgress(status, expectedTaskId);
const missingDependencyIds = outstandingTask ? getMissingDependencyIds(outstandingTask, taskById) : [];
let stallReason = null;
if (paneSuggestsIdle && missingDependencyIds.length > 0) {
stallReason = "missing_dependency";
} else if (paneSuggestsIdle && expectedTaskId !== "" && !hasWorkStartEvidence) {
stallReason = "no_work_start_evidence";
} else if (paneSuggestsIdle && expectedTaskId !== "" && (!statusFresh || !heartbeatFresh)) {
stallReason = "stale_or_missing_worker_reports";
} else if (paneSuggestsIdle && turnsWithoutProgress > 5) {
stallReason = "no_meaningful_turn_progress";
}
if (stallReason) {
nonReportingWorkers.push(w.name);
if (stallReason === "missing_dependency") {
recommendations.push(
`Investigate ${w.name}: task-${outstandingTask?.id ?? expectedTaskId} is blocked by missing task ids [${missingDependencyIds.join(", ")}]; pane is idle at prompt`
);
} else if (stallReason === "no_work_start_evidence") {
recommendations.push(`Investigate ${w.name}: assigned work but no work-start evidence; pane is idle at prompt`);
} else if (stallReason === "stale_or_missing_worker_reports") {
recommendations.push(`Investigate ${w.name}: pane is idle while status/heartbeat are stale or missing`);
} else {
recommendations.push(`Investigate ${w.name}: no meaningful turn progress and pane is idle at prompt`);
}
}
}
const taskCounts = {
total: allTasks.length,
pending: allTasks.filter((t) => t.status === "pending").length,
blocked: allTasks.filter((t) => t.status === "blocked").length,
in_progress: allTasks.filter((t) => t.status === "in_progress").length,
completed: allTasks.filter((t) => t.status === "completed").length,
failed: allTasks.filter((t) => t.status === "failed").length
};
const allTasksTerminal2 = taskCounts.pending === 0 && taskCounts.blocked === 0 && taskCounts.in_progress === 0;
for (const task of allTasks) {
const missingDependencyIds = getMissingDependencyIds(task, taskById);
if (missingDependencyIds.length === 0) {
continue;
}
recommendations.push(
`Investigate task-${task.id}: depends on missing task ids [${missingDependencyIds.join(", ")}]`
);
}
const phase = inferPhase(allTasks.map((t) => ({
status: t.status,
metadata: void 0
})));
await emitMonitorDerivedEvents(
sanitized,
allTasks,
workers.map((w) => ({ name: w.name, alive: w.alive, status: w.status })),
previousSnapshot,
cwd
);
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
const totalMs = import_perf_hooks.performance.now() - monitorStartMs;
await writeMonitorSnapshot(sanitized, {
taskStatusById: Object.fromEntries(allTasks.map((t) => [t.id, t.status])),
workerAliveByName: Object.fromEntries(workers.map((w) => [w.name, w.alive])),
workerStateByName: Object.fromEntries(workers.map((w) => [w.name, w.status.state])),
workerTurnCountByName: Object.fromEntries(workers.map((w) => [w.name, w.heartbeat?.turn_count ?? 0])),
workerTaskIdByName: Object.fromEntries(workers.map((w) => [w.name, w.status.current_task_id ?? ""])),
mailboxNotifiedByMessageId: previousSnapshot?.mailboxNotifiedByMessageId ?? {},
completedEventTaskIds: previousSnapshot?.completedEventTaskIds ?? {},
monitorTimings: {
list_tasks_ms: Number(listTasksMs.toFixed(2)),
worker_scan_ms: Number(workerScanMs.toFixed(2)),
mailbox_delivery_ms: 0,
total_ms: Number(totalMs.toFixed(2)),
updated_at: updatedAt
}
}, cwd);
return {
teamName: sanitized,
phase,
workers,
tasks: {
...taskCounts,
items: allTasks
},
allTasksTerminal: allTasksTerminal2,
deadWorkers,
nonReportingWorkers,
recommendations,
performance: {
list_tasks_ms: Number(listTasksMs.toFixed(2)),
worker_scan_ms: Number(workerScanMs.toFixed(2)),
total_ms: Number(totalMs.toFixed(2)),
updated_at: updatedAt
}
};
}
async function shutdownTeamV2(teamName, cwd, options = {}) {
const logEventFailure = createSwallowedErrorLogger(
"team.runtime-v2.shutdownTeamV2 appendTeamEvent failed"
);
const force = options.force === true;
const ralph = options.ralph === true;
const timeoutMs = options.timeoutMs ?? 15e3;
const sanitized = sanitizeTeamName(teamName);
const config = await readTeamConfig(sanitized, cwd);
if (!config) {
await cleanupTeamState(sanitized, cwd);
return;
}
if (!force) {
const allTasks = await listTasksFromFiles(sanitized, cwd);
const governance = getConfigGovernance(config);
const gate = {
total: allTasks.length,
pending: allTasks.filter((t) => t.status === "pending").length,
blocked: allTasks.filter((t) => t.status === "blocked").length,
in_progress: allTasks.filter((t) => t.status === "in_progress").length,
completed: allTasks.filter((t) => t.status === "completed").length,
failed: allTasks.filter((t) => t.status === "failed").length,
allowed: false
};
gate.allowed = gate.pending === 0 && gate.blocked === 0 && gate.in_progress === 0 && gate.failed === 0;
await appendTeamEvent(sanitized, {
type: "shutdown_gate",
worker: "leader-fixed",
reason: `allowed=${gate.allowed} total=${gate.total} pending=${gate.pending} blocked=${gate.blocked} in_progress=${gate.in_progress} completed=${gate.completed} failed=${gate.failed}${ralph ? " policy=ralph" : ""}`
}, cwd).catch(logEventFailure);
if (!gate.allowed) {
const hasActiveWork = gate.pending > 0 || gate.blocked > 0 || gate.in_progress > 0;
if (!governance.cleanup_requires_all_workers_inactive) {
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `cleanup_override_bypassed:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`
}, cwd).catch(logEventFailure);
} else if (ralph && !hasActiveWork) {
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `gate_bypassed:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`
}, cwd).catch(logEventFailure);
} else {
throw new Error(
`shutdown_gate_blocked:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`
);
}
}
}
if (force) {
await appendTeamEvent(sanitized, {
type: "shutdown_gate_forced",
worker: "leader-fixed",
reason: "force_bypass"
}, cwd).catch(logEventFailure);
}
const shutdownRequestTimes = /* @__PURE__ */ new Map();
for (const w of config.workers) {
try {
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
await writeShutdownRequest(sanitized, w.name, "leader-fixed", cwd);
shutdownRequestTimes.set(w.name, requestedAt);
const shutdownInbox = `# Shutdown Request
All tasks are complete. Please wrap up and respond with a shutdown acknowledgement.
Write your ack to: ${TeamPaths.shutdownAck(sanitized, w.name)}
Format: {"status":"accept","reason":"ok","updated_at":"<iso>"}
Then exit your session.
`;
await writeWorkerInbox(sanitized, w.name, shutdownInbox, cwd);
} catch (err) {
process.stderr.write(`[team/runtime-v2] shutdown request failed for ${w.name}: ${err}
`);
}
}
const deadline = Date.now() + timeoutMs;
const rejected = [];
const ackedWorkers = /* @__PURE__ */ new Set();
while (Date.now() < deadline) {
for (const w of config.workers) {
if (ackedWorkers.has(w.name)) continue;
const ack = await readShutdownAck(sanitized, w.name, cwd, shutdownRequestTimes.get(w.name));
if (ack) {
ackedWorkers.add(w.name);
await appendTeamEvent(sanitized, {
type: "shutdown_ack",
worker: w.name,
reason: ack.status === "reject" ? `reject:${ack.reason || "no_reason"}` : "accept"
}, cwd).catch(logEventFailure);
if (ack.status === "reject") {
rejected.push({ worker: w.name, reason: ack.reason || "no_reason" });
}
}
}
if (rejected.length > 0 && !force) {
const detail = rejected.map((r) => `${r.worker}:${r.reason}`).join(",");
throw new Error(`shutdown_rejected:${detail}`);
}
const allDone = config.workers.every((w) => ackedWorkers.has(w.name));
if (allDone) break;
await new Promise((r) => setTimeout(r, 2e3));
}
try {
const { killWorkerPanes: killWorkerPanes2, killTeamSession: killTeamSession2, resolveSplitPaneWorkerPaneIds: resolveSplitPaneWorkerPaneIds2 } = await Promise.resolve().then(() => (init_tmux_session(), tmux_session_exports));
const recordedWorkerPaneIds = config.workers.map((w) => w.pane_id).filter((p) => typeof p === "string" && p.trim().length > 0);
const ownsWindow = config.tmux_window_owned === true;
const workerPaneIds = ownsWindow ? recordedWorkerPaneIds : await resolveSplitPaneWorkerPaneIds2(
config.tmux_session,
recordedWorkerPaneIds,
config.leader_pane_id ?? void 0
);
await killWorkerPanes2({
paneIds: workerPaneIds,
leaderPaneId: config.leader_pane_id ?? void 0,
teamName: sanitized,
cwd
});
if (config.tmux_session && (ownsWindow || !config.tmux_session.includes(":"))) {
const sessionMode = ownsWindow ? config.tmux_session.includes(":") ? "dedicated-window" : "detached-session" : "detached-session";
await killTeamSession2(
config.tmux_session,
workerPaneIds,
config.leader_pane_id ?? void 0,
{ sessionMode }
);
}
} catch (err) {
process.stderr.write(`[team/runtime-v2] tmux cleanup: ${err}
`);
}
if (ralph) {
const finalTasks = await listTasksFromFiles(sanitized, cwd).catch(() => []);
const completed = finalTasks.filter((t) => t.status === "completed").length;
const failed = finalTasks.filter((t) => t.status === "failed").length;
const pending = finalTasks.filter((t) => t.status === "pending").length;
await appendTeamEvent(sanitized, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: `ralph_cleanup_summary: total=${finalTasks.length} completed=${completed} failed=${failed} pending=${pending} force=${force}`
}, cwd).catch(logEventFailure);
}
try {
cleanupTeamWorktrees(sanitized, cwd);
} catch (err) {
process.stderr.write(`[team/runtime-v2] worktree cleanup: ${err}
`);
}
await cleanupTeamState(sanitized, cwd);
}
// src/team/runtime-cli.ts
function getTerminalStatus(taskCounts, expectedTaskCount) {
const active = taskCounts.pending + taskCounts.inProgress;
const terminal = taskCounts.completed + taskCounts.failed;
if (active !== 0 || terminal !== expectedTaskCount) return null;
return taskCounts.failed > 0 ? "failed" : "completed";
}
function parseWatchdogFailedAt(marker) {
if (typeof marker.failedAt === "number") return marker.failedAt;
if (typeof marker.failedAt === "string") {
const numeric = Number(marker.failedAt);
if (Number.isFinite(numeric)) return numeric;
const parsed = Date.parse(marker.failedAt);
if (Number.isFinite(parsed)) return parsed;
}
throw new Error("watchdog marker missing valid failedAt");
}
async function checkWatchdogFailedMarker(stateRoot2, startTime) {
const markerPath = (0, import_path20.join)(stateRoot2, "watchdog-failed.json");
let raw;
try {
raw = await (0, import_promises8.readFile)(markerPath, "utf-8");
} catch (err) {
const code = err.code;
if (code === "ENOENT") return { failed: false };
return { failed: true, reason: `Failed to read watchdog marker: ${err}` };
}
let marker;
try {
marker = JSON.parse(raw);
} catch (err) {
return { failed: true, reason: `Failed to parse watchdog marker: ${err}` };
}
let failedAt;
try {
failedAt = parseWatchdogFailedAt(marker);
} catch (err) {
return { failed: true, reason: `Invalid watchdog marker: ${err}` };
}
if (failedAt >= startTime) {
return { failed: true, reason: `Watchdog marked team failed at ${new Date(failedAt).toISOString()}` };
}
try {
await (0, import_promises8.unlink)(markerPath);
} catch {
}
return { failed: false };
}
async function writeResultArtifact(output, finishedAt, jobId = process.env.OMC_JOB_ID, omcJobsDir = process.env.OMC_JOBS_DIR) {
if (!jobId || !omcJobsDir) return;
const resultPath = (0, import_path20.join)(omcJobsDir, `${jobId}-result.json`);
const tmpPath = `${resultPath}.tmp`;
await (0, import_promises8.writeFile)(
tmpPath,
JSON.stringify({ ...output, finishedAt }),
"utf-8"
);
await (0, import_promises8.rename)(tmpPath, resultPath);
}
function buildCliOutput(stateRoot2, teamName, status, workerCount, startTimeMs) {
const taskResults = collectTaskResults(stateRoot2);
const duration = (Date.now() - startTimeMs) / 1e3;
return {
status,
teamName,
taskResults,
duration,
workerCount
};
}
function buildTerminalCliResult(stateRoot2, teamName, phase, workerCount, startTimeMs) {
const status = phase === "complete" ? "completed" : "failed";
return {
output: buildCliOutput(stateRoot2, teamName, status, workerCount, startTimeMs),
exitCode: status === "completed" ? 0 : 1,
notice: `[runtime-cli] phase=${phase} reached terminal state; preserving team state for inspection. Run "omc team shutdown ${teamName}" when explicit cleanup is desired.
`
};
}
async function writePanesFile(jobId, paneIds, leaderPaneId, sessionName2, ownsWindow) {
const omcJobsDir = process.env.OMC_JOBS_DIR;
if (!jobId || !omcJobsDir) return;
const panesPath = (0, import_path20.join)(omcJobsDir, `${jobId}-panes.json`);
await (0, import_promises8.writeFile)(
panesPath + ".tmp",
JSON.stringify({ paneIds: [...paneIds], leaderPaneId, sessionName: sessionName2, ownsWindow })
);
await (0, import_promises8.rename)(panesPath + ".tmp", panesPath);
}
function collectTaskResults(stateRoot2) {
const tasksDir = (0, import_path20.join)(stateRoot2, "tasks");
try {
const files = (0, import_fs18.readdirSync)(tasksDir).filter((f) => f.endsWith(".json"));
return files.map((f) => {
try {
const raw = (0, import_fs18.readFileSync)((0, import_path20.join)(tasksDir, f), "utf-8");
const task = JSON.parse(raw);
return {
taskId: task.id ?? f.replace(".json", ""),
status: task.status ?? "unknown",
summary: task.result ?? task.summary ?? ""
};
} catch {
return { taskId: f.replace(".json", ""), status: "unknown", summary: "" };
}
});
} catch {
return [];
}
}
async function main() {
const startTime = Date.now();
const logLeaderNudgeEventFailure = createSwallowedErrorLogger(
"team.runtime-cli main appendTeamEvent failed"
);
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const rawInput = Buffer.concat(chunks).toString("utf-8").trim();
let input;
try {
input = JSON.parse(rawInput);
} catch (err) {
process.stderr.write(`[runtime-cli] Failed to parse stdin JSON: ${err}
`);
process.exit(1);
}
const missing = [];
if (!input.teamName) missing.push("teamName");
if (!input.agentTypes || !Array.isArray(input.agentTypes) || input.agentTypes.length === 0) missing.push("agentTypes");
if (!input.tasks || !Array.isArray(input.tasks) || input.tasks.length === 0) missing.push("tasks");
if (!input.cwd) missing.push("cwd");
if (missing.length > 0) {
process.stderr.write(`[runtime-cli] Missing required fields: ${missing.join(", ")}
`);
process.exit(1);
}
const {
teamName,
agentTypes,
tasks,
cwd,
newWindow = false,
pollIntervalMs = 5e3,
sentinelGateTimeoutMs = 3e4,
sentinelGatePollIntervalMs = 250
} = input;
const workerCount = input.workerCount ?? agentTypes.length;
const stateRoot2 = (0, import_path20.join)(cwd, `.omc/state/team/${teamName}`);
const config = {
teamName,
workerCount,
agentTypes,
tasks,
cwd,
newWindow
};
const useV2 = isRuntimeV2Enabled();
let runtime = null;
let finalStatus = "failed";
let pollActive = true;
async function doShutdown(status) {
pollActive = false;
finalStatus = status;
if (!useV2 && runtime?.stopWatchdog) {
runtime.stopWatchdog();
}
if (runtime) {
try {
if (useV2) {
await shutdownTeamV2(runtime.teamName, runtime.cwd, { force: true });
} else {
await shutdownTeam(
runtime.teamName,
runtime.sessionName,
runtime.cwd,
2e3,
runtime.workerPaneIds,
runtime.leaderPaneId,
runtime.ownsWindow
);
}
} catch (err) {
process.stderr.write(`[runtime-cli] shutdown error: ${err}
`);
}
}
const output = buildCliOutput(stateRoot2, teamName, finalStatus, workerCount, startTime);
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
try {
await writeResultArtifact(output, finishedAt);
} catch (err) {
process.stderr.write(`[runtime-cli] Failed to persist result artifact: ${err}
`);
}
process.stdout.write(JSON.stringify(output) + "\n");
process.exit(status === "completed" ? 0 : 1);
}
function exitWithoutShutdown(phase) {
pollActive = false;
finalStatus = phase === "complete" ? "completed" : "failed";
const result = buildTerminalCliResult(stateRoot2, teamName, phase, workerCount, startTime);
process.stderr.write(result.notice);
process.stdout.write(JSON.stringify(result.output) + "\n");
process.exit(result.exitCode);
}
process.on("SIGINT", () => {
process.stderr.write("[runtime-cli] Received SIGINT, shutting down...\n");
doShutdown("failed").catch(() => process.exit(1));
});
process.on("SIGTERM", () => {
process.stderr.write("[runtime-cli] Received SIGTERM, shutting down...\n");
doShutdown("failed").catch(() => process.exit(1));
});
try {
if (useV2) {
const v2Runtime = await startTeamV2({
teamName,
workerCount,
agentTypes,
tasks,
cwd,
newWindow
});
const v2PaneIds = v2Runtime.config.workers.map((w) => w.pane_id).filter((p) => typeof p === "string");
runtime = {
teamName: v2Runtime.teamName,
sessionName: v2Runtime.sessionName,
leaderPaneId: v2Runtime.config.leader_pane_id || "",
ownsWindow: v2Runtime.ownsWindow,
config,
workerNames: v2Runtime.config.workers.map((w) => w.name),
workerPaneIds: v2PaneIds,
activeWorkers: /* @__PURE__ */ new Map(),
cwd
};
} else {
runtime = await startTeam(config);
}
} catch (err) {
process.stderr.write(`[runtime-cli] startTeam failed: ${err}
`);
process.exit(1);
}
const jobId = process.env.OMC_JOB_ID;
const expectedTaskCount = tasks.length;
let mismatchStreak = 0;
try {
await writePanesFile(jobId, runtime.workerPaneIds, runtime.leaderPaneId, runtime.sessionName, Boolean(runtime.ownsWindow));
} catch (err) {
process.stderr.write(`[runtime-cli] Failed to persist pane IDs: ${err}
`);
}
if (useV2) {
process.stderr.write("[runtime-cli] Using runtime v2 (event-driven, no watchdog)\n");
let lastLeaderNudgeReason = "";
while (pollActive) {
await new Promise((r) => setTimeout(r, pollIntervalMs));
if (!pollActive) break;
let snap;
try {
snap = await monitorTeamV2(teamName, cwd);
} catch (err) {
process.stderr.write(`[runtime-cli/v2] monitorTeamV2 error: ${err}
`);
continue;
}
if (!snap) {
process.stderr.write("[runtime-cli/v2] monitorTeamV2 returned null (team config missing?)\n");
await doShutdown("failed");
return;
}
try {
await writePanesFile(jobId, runtime.workerPaneIds, runtime.leaderPaneId, runtime.sessionName, Boolean(runtime.ownsWindow));
} catch {
}
process.stderr.write(
`[runtime-cli/v2] phase=${snap.phase} pending=${snap.tasks.pending} blocked=${snap.tasks.blocked} in_progress=${snap.tasks.in_progress} completed=${snap.tasks.completed} failed=${snap.tasks.failed} dead=${snap.deadWorkers.length} totalMs=${snap.performance.total_ms}
`
);
const leaderGuidance = deriveTeamLeaderGuidance({
tasks: {
pending: snap.tasks.pending,
blocked: snap.tasks.blocked,
inProgress: snap.tasks.in_progress,
completed: snap.tasks.completed,
failed: snap.tasks.failed
},
workers: {
total: snap.workers.length,
alive: snap.workers.filter((worker) => worker.alive).length,
idle: snap.workers.filter((worker) => worker.alive && (worker.status.state === "idle" || worker.status.state === "done")).length,
nonReporting: snap.nonReportingWorkers.length
}
});
process.stderr.write(
`[runtime-cli/v2] leader_next_action=${leaderGuidance.nextAction} reason=${leaderGuidance.reason}
`
);
for (const recommendation of snap.recommendations) {
process.stderr.write(`[runtime-cli/v2] recommendation=${recommendation}
`);
}
if (leaderGuidance.nextAction === "keep-checking-status") {
lastLeaderNudgeReason = "";
}
if (leaderGuidance.nextAction !== "keep-checking-status" && leaderGuidance.reason !== lastLeaderNudgeReason) {
await appendTeamEvent(teamName, {
type: "team_leader_nudge",
worker: "leader-fixed",
reason: leaderGuidance.reason,
next_action: leaderGuidance.nextAction,
message: leaderGuidance.message
}, cwd).catch(logLeaderNudgeEventFailure);
lastLeaderNudgeReason = leaderGuidance.reason;
}
const v2Observed = snap.tasks.pending + snap.tasks.in_progress + snap.tasks.completed + snap.tasks.failed;
if (v2Observed !== expectedTaskCount) {
mismatchStreak += 1;
process.stderr.write(
`[runtime-cli/v2] Task-count mismatch observed=${v2Observed} expected=${expectedTaskCount} streak=${mismatchStreak}
`
);
if (mismatchStreak >= 2) {
process.stderr.write("[runtime-cli/v2] Persistent task-count mismatch \u2014 failing fast\n");
await doShutdown("failed");
return;
}
continue;
}
mismatchStreak = 0;
if (snap.phase === "completed") {
exitWithoutShutdown("complete");
return;
}
if (snap.phase === "failed") {
exitWithoutShutdown("failed");
return;
}
if (snap.allTasksTerminal) {
const hasFailures = snap.tasks.failed > 0;
if (!hasFailures) {
const sentinelLogPath = (0, import_path20.join)(cwd, "sentinel_stop.jsonl");
const gateResult = await waitForSentinelReadiness({
workspace: cwd,
logPath: sentinelLogPath,
timeoutMs: sentinelGateTimeoutMs,
pollIntervalMs: sentinelGatePollIntervalMs
});
if (!gateResult.ready) {
process.stderr.write(
`[runtime-cli/v2] Sentinel gate blocked: ${gateResult.blockers.join("; ")}
`
);
exitWithoutShutdown("failed");
return;
}
exitWithoutShutdown("complete");
} else {
process.stderr.write("[runtime-cli/v2] Terminal failure detected from task counts\n");
exitWithoutShutdown("failed");
}
return;
}
const allDead = runtime.workerPaneIds.length > 0 && snap.deadWorkers.length === runtime.workerPaneIds.length;
const hasOutstanding = snap.tasks.pending + snap.tasks.in_progress > 0;
if (allDead && hasOutstanding) {
process.stderr.write("[runtime-cli/v2] All workers dead with outstanding work \u2014 failing\n");
await doShutdown("failed");
return;
}
}
return;
}
while (pollActive) {
await new Promise((r) => setTimeout(r, pollIntervalMs));
if (!pollActive) break;
const watchdogCheck = await checkWatchdogFailedMarker(stateRoot2, startTime);
if (watchdogCheck.failed) {
process.stderr.write(`[runtime-cli] ${watchdogCheck.reason ?? "Watchdog failure marker detected"}
`);
await doShutdown("failed");
return;
}
let snap;
try {
snap = await monitorTeam(teamName, cwd, runtime.workerPaneIds);
} catch (err) {
process.stderr.write(`[runtime-cli] monitorTeam error: ${err}
`);
continue;
}
try {
await writePanesFile(jobId, runtime.workerPaneIds, runtime.leaderPaneId, runtime.sessionName, Boolean(runtime.ownsWindow));
} catch (err) {
process.stderr.write(`[runtime-cli] Failed to persist pane IDs: ${err}
`);
}
process.stderr.write(
`[runtime-cli] phase=${snap.phase} pending=${snap.taskCounts.pending} inProgress=${snap.taskCounts.inProgress} completed=${snap.taskCounts.completed} failed=${snap.taskCounts.failed} dead=${snap.deadWorkers.length} monitorMs=${snap.monitorPerformance.totalMs} tasksMs=${snap.monitorPerformance.listTasksMs} workerMs=${snap.monitorPerformance.workerScanMs}
`
);
const observedTaskCount = snap.taskCounts.pending + snap.taskCounts.inProgress + snap.taskCounts.completed + snap.taskCounts.failed;
if (observedTaskCount !== expectedTaskCount) {
mismatchStreak += 1;
process.stderr.write(
`[runtime-cli] Task-count mismatch observed=${observedTaskCount} expected=${expectedTaskCount} streak=${mismatchStreak}
`
);
if (mismatchStreak >= 2) {
process.stderr.write("[runtime-cli] Persistent task-count mismatch detected \u2014 failing fast\n");
await doShutdown("failed");
return;
}
continue;
}
mismatchStreak = 0;
const terminalStatus = getTerminalStatus(snap.taskCounts, expectedTaskCount);
if (terminalStatus === "completed") {
const sentinelLogPath = (0, import_path20.join)(cwd, "sentinel_stop.jsonl");
const gateResult = await waitForSentinelReadiness({
workspace: cwd,
logPath: sentinelLogPath,
timeoutMs: sentinelGateTimeoutMs,
pollIntervalMs: sentinelGatePollIntervalMs
});
if (!gateResult.ready) {
process.stderr.write(
`[runtime-cli] Sentinel gate blocked completion (timedOut=${gateResult.timedOut}, attempts=${gateResult.attempts}, elapsedMs=${gateResult.elapsedMs}): ${gateResult.blockers.join("; ")}
`
);
await doShutdown("failed");
return;
}
await doShutdown("completed");
return;
}
if (terminalStatus === "failed") {
process.stderr.write("[runtime-cli] Terminal failure detected from task counts\n");
await doShutdown("failed");
return;
}
const allWorkersDead = runtime.workerPaneIds.length > 0 && snap.deadWorkers.length === runtime.workerPaneIds.length;
const hasOutstandingWork = snap.taskCounts.pending + snap.taskCounts.inProgress > 0;
const deadWorkerFailure = allWorkersDead && hasOutstandingWork;
const fixingWithNoWorkers = snap.phase === "fixing" && allWorkersDead;
if (deadWorkerFailure || fixingWithNoWorkers) {
process.stderr.write(`[runtime-cli] Failure detected: deadWorkerFailure=${deadWorkerFailure} fixingWithNoWorkers=${fixingWithNoWorkers}
`);
exitWithoutShutdown("failed");
return;
}
}
}
if (require.main === module) {
main().catch((err) => {
process.stderr.write(`[runtime-cli] Fatal error: ${err}
`);
process.exit(1);
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
buildCliOutput,
buildTerminalCliResult,
checkWatchdogFailedMarker,
getTerminalStatus,
writeResultArtifact
});