@h-rig/cli 0.0.6-alpha.21 → 0.0.6-alpha.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig.js +940 -387
- package/dist/src/commands/_operator-view.js +539 -4
- package/dist/src/commands/_pi-frontend.js +727 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +645 -0
- package/dist/src/commands/_preflight.js +1 -81
- package/dist/src/commands/_server-client.js +80 -1
- package/dist/src/commands/run.js +541 -115
- package/dist/src/commands/task-run-driver.js +141 -10
- package/dist/src/commands/task.js +544 -197
- package/dist/src/commands.js +940 -387
- package/dist/src/index.js +940 -387
- package/package.json +6 -5
- package/dist/src/commands/_pi-session.js +0 -253
package/dist/bin/rig.js
CHANGED
|
@@ -2954,6 +2954,66 @@ async function steerRunViaServer(context, runId, message) {
|
|
|
2954
2954
|
});
|
|
2955
2955
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
2956
2956
|
}
|
|
2957
|
+
async function getRunPiSessionViaServer(context, runId) {
|
|
2958
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi`);
|
|
2959
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2960
|
+
}
|
|
2961
|
+
async function getRunPiMessagesViaServer(context, runId) {
|
|
2962
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/messages`);
|
|
2963
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { messages: [] };
|
|
2964
|
+
}
|
|
2965
|
+
async function getRunPiStatusViaServer(context, runId) {
|
|
2966
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/status`);
|
|
2967
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2968
|
+
}
|
|
2969
|
+
async function getRunPiCommandsViaServer(context, runId) {
|
|
2970
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
|
|
2971
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
|
|
2972
|
+
}
|
|
2973
|
+
async function sendRunPiPromptViaServer(context, runId, text2, streamingBehavior) {
|
|
2974
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
|
|
2975
|
+
method: "POST",
|
|
2976
|
+
headers: { "content-type": "application/json" },
|
|
2977
|
+
body: JSON.stringify({ text: text2, streamingBehavior })
|
|
2978
|
+
});
|
|
2979
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2980
|
+
}
|
|
2981
|
+
async function sendRunPiShellViaServer(context, runId, text2) {
|
|
2982
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/shell`, {
|
|
2983
|
+
method: "POST",
|
|
2984
|
+
headers: { "content-type": "application/json" },
|
|
2985
|
+
body: JSON.stringify({ text: text2 })
|
|
2986
|
+
});
|
|
2987
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2988
|
+
}
|
|
2989
|
+
async function runRunPiCommandViaServer(context, runId, text2) {
|
|
2990
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/run`, {
|
|
2991
|
+
method: "POST",
|
|
2992
|
+
headers: { "content-type": "application/json" },
|
|
2993
|
+
body: JSON.stringify({ text: text2 })
|
|
2994
|
+
});
|
|
2995
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
2996
|
+
}
|
|
2997
|
+
async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
|
|
2998
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
|
|
2999
|
+
method: "POST",
|
|
3000
|
+
headers: { "content-type": "application/json" },
|
|
3001
|
+
body: JSON.stringify({ requestId, ...valueOrCancel })
|
|
3002
|
+
});
|
|
3003
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
3004
|
+
}
|
|
3005
|
+
async function abortRunPiViaServer(context, runId) {
|
|
3006
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/abort`, { method: "POST" });
|
|
3007
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { aborted: true };
|
|
3008
|
+
}
|
|
3009
|
+
async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
3010
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
3011
|
+
const url = new URL(`${server.baseUrl.replace(/\/+$/, "")}/api/runs/${encodeURIComponent(runId)}/pi/events`);
|
|
3012
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
3013
|
+
if (server.authToken)
|
|
3014
|
+
url.searchParams.set("token", server.authToken);
|
|
3015
|
+
return url.toString();
|
|
3016
|
+
}
|
|
2957
3017
|
async function submitTaskRunViaServer(context, input) {
|
|
2958
3018
|
const isTaskRun = Boolean(input.taskId);
|
|
2959
3019
|
const endpoint = isTaskRun ? "/api/runs/task" : "/api/runs/adhoc";
|
|
@@ -2986,183 +3046,6 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2986
3046
|
return { runId };
|
|
2987
3047
|
}
|
|
2988
3048
|
|
|
2989
|
-
// packages/cli/src/commands/_pi-install.ts
|
|
2990
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2991
|
-
import { homedir as homedir3 } from "os";
|
|
2992
|
-
import { resolve as resolve10 } from "path";
|
|
2993
|
-
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
2994
|
-
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2995
|
-
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2996
|
-
export { default } from '@rig/pi-rig';
|
|
2997
|
-
`;
|
|
2998
|
-
async function defaultCommandRunner(command, options = {}) {
|
|
2999
|
-
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
3000
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
3001
|
-
new Response(proc.stdout).text(),
|
|
3002
|
-
new Response(proc.stderr).text(),
|
|
3003
|
-
proc.exited
|
|
3004
|
-
]);
|
|
3005
|
-
return { exitCode, stdout, stderr };
|
|
3006
|
-
}
|
|
3007
|
-
function resolvePiRigExtensionPath(homeDir) {
|
|
3008
|
-
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
3009
|
-
}
|
|
3010
|
-
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
3011
|
-
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
3012
|
-
if (exists(resolve10(localPackage, "package.json")))
|
|
3013
|
-
return localPackage;
|
|
3014
|
-
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
3015
|
-
}
|
|
3016
|
-
function resolvePiHomeDir(inputHomeDir) {
|
|
3017
|
-
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir3();
|
|
3018
|
-
}
|
|
3019
|
-
function piListContainsPiRig(output) {
|
|
3020
|
-
return output.split(/\r?\n/).some((line) => {
|
|
3021
|
-
const normalized = line.trim();
|
|
3022
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
3023
|
-
});
|
|
3024
|
-
}
|
|
3025
|
-
async function safeRun(runner, command, options) {
|
|
3026
|
-
try {
|
|
3027
|
-
return await runner(command, options);
|
|
3028
|
-
} catch (error) {
|
|
3029
|
-
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
|
-
function splitInstallCommand(value) {
|
|
3033
|
-
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
3034
|
-
}
|
|
3035
|
-
async function ensurePiBinaryAvailable(input) {
|
|
3036
|
-
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
3037
|
-
if (current.exitCode === 0) {
|
|
3038
|
-
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
3039
|
-
if (updateCommand) {
|
|
3040
|
-
const parts2 = splitInstallCommand(updateCommand);
|
|
3041
|
-
if (parts2.length > 0)
|
|
3042
|
-
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3043
|
-
}
|
|
3044
|
-
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
3045
|
-
}
|
|
3046
|
-
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
3047
|
-
const parts = splitInstallCommand(installCommand);
|
|
3048
|
-
if (parts.length === 0) {
|
|
3049
|
-
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
3050
|
-
}
|
|
3051
|
-
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3052
|
-
if (install.exitCode !== 0) {
|
|
3053
|
-
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
3054
|
-
}
|
|
3055
|
-
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
3056
|
-
return {
|
|
3057
|
-
ok: next.exitCode === 0,
|
|
3058
|
-
installedOrUpdated: true,
|
|
3059
|
-
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
3060
|
-
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
3061
|
-
};
|
|
3062
|
-
}
|
|
3063
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
3064
|
-
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
3065
|
-
const indexPath = resolve10(extensionPath, "index.ts");
|
|
3066
|
-
if (!exists(indexPath))
|
|
3067
|
-
return;
|
|
3068
|
-
try {
|
|
3069
|
-
const content = readFileSync4(indexPath, "utf8");
|
|
3070
|
-
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3071
|
-
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3072
|
-
}
|
|
3073
|
-
} catch {}
|
|
3074
|
-
}
|
|
3075
|
-
async function checkPiRigInstall(input = {}) {
|
|
3076
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
3077
|
-
const extensionPath = resolvePiRigExtensionPath(home);
|
|
3078
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
3079
|
-
return {
|
|
3080
|
-
extensionPath,
|
|
3081
|
-
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
3082
|
-
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3083
|
-
};
|
|
3084
|
-
}
|
|
3085
|
-
const exists = input.exists ?? existsSync6;
|
|
3086
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3087
|
-
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3088
|
-
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3089
|
-
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3090
|
-
${piListResult.stderr}`);
|
|
3091
|
-
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3092
|
-
const hasPiRig = listedPiRig;
|
|
3093
|
-
return {
|
|
3094
|
-
extensionPath,
|
|
3095
|
-
pi: {
|
|
3096
|
-
ok: piResult.exitCode === 0,
|
|
3097
|
-
label: "pi",
|
|
3098
|
-
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
3099
|
-
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
3100
|
-
},
|
|
3101
|
-
piRig: {
|
|
3102
|
-
ok: hasPiRig,
|
|
3103
|
-
label: "pi-rig global extension",
|
|
3104
|
-
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
3105
|
-
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
3106
|
-
}
|
|
3107
|
-
};
|
|
3108
|
-
}
|
|
3109
|
-
async function ensurePiRigInstalled(input) {
|
|
3110
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
3111
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
3112
|
-
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
3113
|
-
return { ...status2, installedPath: status2.extensionPath };
|
|
3114
|
-
}
|
|
3115
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3116
|
-
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
3117
|
-
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
3118
|
-
extensionPath: resolvePiRigExtensionPath(home),
|
|
3119
|
-
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
3120
|
-
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
3121
|
-
};
|
|
3122
|
-
if (!piAvailable.ok) {
|
|
3123
|
-
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
3124
|
-
}
|
|
3125
|
-
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
3126
|
-
removeManagedLegacyPiRigBridge(home);
|
|
3127
|
-
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
3128
|
-
if (install.exitCode !== 0) {
|
|
3129
|
-
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
3130
|
-
}
|
|
3131
|
-
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
3132
|
-
return { ...next, installedPath: packageSource };
|
|
3133
|
-
}
|
|
3134
|
-
async function ensureRemotePiRigInstalled(input) {
|
|
3135
|
-
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
3136
|
-
method: "POST",
|
|
3137
|
-
headers: { "content-type": "application/json" },
|
|
3138
|
-
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
3139
|
-
});
|
|
3140
|
-
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
3141
|
-
const piOk = record.piOk === true || record.ok === true;
|
|
3142
|
-
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
3143
|
-
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
3144
|
-
return {
|
|
3145
|
-
remote: true,
|
|
3146
|
-
extensionPath,
|
|
3147
|
-
pi: {
|
|
3148
|
-
ok: piOk,
|
|
3149
|
-
label: "pi",
|
|
3150
|
-
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
3151
|
-
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
3152
|
-
},
|
|
3153
|
-
piRig: {
|
|
3154
|
-
ok: piRigOk,
|
|
3155
|
-
label: "pi-rig global extension",
|
|
3156
|
-
detail: extensionPath,
|
|
3157
|
-
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
3158
|
-
}
|
|
3159
|
-
};
|
|
3160
|
-
}
|
|
3161
|
-
async function buildPiSetupChecks(input = {}) {
|
|
3162
|
-
const status = await checkPiRigInstall(input);
|
|
3163
|
-
return [status.pi, status.piRig];
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
3049
|
// packages/cli/src/commands/_preflight.ts
|
|
3167
3050
|
function preflightCheck(id, label, status, detail, remediation) {
|
|
3168
3051
|
return {
|
|
@@ -3320,14 +3203,7 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
3320
3203
|
}
|
|
3321
3204
|
}
|
|
3322
3205
|
if ((options.runtimeAdapter ?? "pi") === "pi") {
|
|
3323
|
-
|
|
3324
|
-
ok: false,
|
|
3325
|
-
label: "pi/pi-rig checks",
|
|
3326
|
-
hint: message(error)
|
|
3327
|
-
}]);
|
|
3328
|
-
for (const pi of piChecks) {
|
|
3329
|
-
checks.push(preflightCheck(pi.label === "pi" ? "pi" : "pi-rig", pi.label, pi.ok ? "pass" : "fail", pi.detail, pi.hint ?? (pi.ok ? undefined : "Run `rig init --yes` to install/update Pi and enable pi-rig.")));
|
|
3330
|
-
}
|
|
3206
|
+
checks.push(preflightCheck("runtime", "worker Pi SDK session daemon", "pass", selectedServer?.connectionKind === "remote" ? "remote worker-owned runtime" : "bundled server-owned runtime"));
|
|
3331
3207
|
} else {
|
|
3332
3208
|
checks.push(preflightCheck("runtime", "runtime adapter", "pass", options.runtimeAdapter));
|
|
3333
3209
|
}
|
|
@@ -3443,7 +3319,7 @@ async function executeQueue(context, args) {
|
|
|
3443
3319
|
}
|
|
3444
3320
|
|
|
3445
3321
|
// packages/cli/src/commands/agent.ts
|
|
3446
|
-
import { resolve as
|
|
3322
|
+
import { resolve as resolve11 } from "path";
|
|
3447
3323
|
import {
|
|
3448
3324
|
agentId,
|
|
3449
3325
|
cleanupAgentRuntime,
|
|
@@ -3453,8 +3329,8 @@ import {
|
|
|
3453
3329
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3454
3330
|
|
|
3455
3331
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3456
|
-
import { existsSync as
|
|
3457
|
-
import { resolve as
|
|
3332
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3333
|
+
import { resolve as resolve10 } from "path";
|
|
3458
3334
|
import {
|
|
3459
3335
|
readAuthorityRun,
|
|
3460
3336
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3476,8 +3352,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3476
3352
|
return "claude-code";
|
|
3477
3353
|
}
|
|
3478
3354
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3479
|
-
const issuesPath =
|
|
3480
|
-
if (!
|
|
3355
|
+
const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3356
|
+
if (!existsSync6(issuesPath)) {
|
|
3481
3357
|
return null;
|
|
3482
3358
|
}
|
|
3483
3359
|
let latest = null;
|
|
@@ -3545,7 +3421,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3545
3421
|
} else if ("errorText" in next) {
|
|
3546
3422
|
delete next.errorText;
|
|
3547
3423
|
}
|
|
3548
|
-
writeJsonFile3(
|
|
3424
|
+
writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3549
3425
|
return next;
|
|
3550
3426
|
}
|
|
3551
3427
|
|
|
@@ -3666,10 +3542,10 @@ async function executeAgent(context, args) {
|
|
|
3666
3542
|
status: "running",
|
|
3667
3543
|
startedAt: createdAt,
|
|
3668
3544
|
worktreePath: runtime.workspaceDir,
|
|
3669
|
-
artifactRoot:
|
|
3545
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3670
3546
|
logRoot: runtime.logsDir,
|
|
3671
|
-
sessionPath:
|
|
3672
|
-
sessionLogPath:
|
|
3547
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3548
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3673
3549
|
pid: process.pid
|
|
3674
3550
|
});
|
|
3675
3551
|
const result = await runInAgentRuntime({
|
|
@@ -3689,10 +3565,10 @@ async function executeAgent(context, args) {
|
|
|
3689
3565
|
startedAt: createdAt,
|
|
3690
3566
|
completedAt: failedAt,
|
|
3691
3567
|
worktreePath: runtime.workspaceDir,
|
|
3692
|
-
artifactRoot:
|
|
3568
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3693
3569
|
logRoot: runtime.logsDir,
|
|
3694
|
-
sessionPath:
|
|
3695
|
-
sessionLogPath:
|
|
3570
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3571
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3696
3572
|
pid: process.pid,
|
|
3697
3573
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3698
3574
|
});
|
|
@@ -3709,10 +3585,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3709
3585
|
startedAt: createdAt,
|
|
3710
3586
|
completedAt,
|
|
3711
3587
|
worktreePath: runtime.workspaceDir,
|
|
3712
|
-
artifactRoot:
|
|
3588
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3713
3589
|
logRoot: runtime.logsDir,
|
|
3714
|
-
sessionPath:
|
|
3715
|
-
sessionLogPath:
|
|
3590
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3591
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3716
3592
|
pid: process.pid
|
|
3717
3593
|
});
|
|
3718
3594
|
return {
|
|
@@ -3786,17 +3662,17 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3786
3662
|
import {
|
|
3787
3663
|
chmodSync,
|
|
3788
3664
|
copyFileSync as copyFileSync2,
|
|
3789
|
-
existsSync as
|
|
3665
|
+
existsSync as existsSync7,
|
|
3790
3666
|
mkdirSync as mkdirSync6,
|
|
3791
3667
|
readdirSync,
|
|
3792
3668
|
readlinkSync,
|
|
3793
|
-
rmSync as
|
|
3669
|
+
rmSync as rmSync3,
|
|
3794
3670
|
statSync,
|
|
3795
3671
|
symlinkSync,
|
|
3796
3672
|
unlinkSync
|
|
3797
3673
|
} from "fs";
|
|
3798
|
-
import { homedir as
|
|
3799
|
-
import { resolve as
|
|
3674
|
+
import { homedir as homedir3 } from "os";
|
|
3675
|
+
import { resolve as resolve12 } from "path";
|
|
3800
3676
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3801
3677
|
import {
|
|
3802
3678
|
computeRuntimeImageFingerprint,
|
|
@@ -3815,13 +3691,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3815
3691
|
|
|
3816
3692
|
// packages/cli/src/commands/dist.ts
|
|
3817
3693
|
function collectRigValidatorBuildTargets(input) {
|
|
3818
|
-
const validatorsRoot =
|
|
3819
|
-
if (!
|
|
3694
|
+
const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3695
|
+
if (!existsSync7(validatorsRoot))
|
|
3820
3696
|
return [];
|
|
3821
3697
|
const targets = [];
|
|
3822
3698
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3823
3699
|
for (const category of categories) {
|
|
3824
|
-
const categoryDir =
|
|
3700
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3825
3701
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3826
3702
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3827
3703
|
continue;
|
|
@@ -3830,7 +3706,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3830
3706
|
continue;
|
|
3831
3707
|
targets.push({
|
|
3832
3708
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3833
|
-
dest:
|
|
3709
|
+
dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3834
3710
|
cwd: input.hostProjectRoot
|
|
3835
3711
|
});
|
|
3836
3712
|
}
|
|
@@ -3839,16 +3715,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3839
3715
|
}
|
|
3840
3716
|
async function findLatestDistBinary(projectRoot) {
|
|
3841
3717
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3842
|
-
if (!
|
|
3718
|
+
if (!existsSync7(distRoot)) {
|
|
3843
3719
|
return null;
|
|
3844
3720
|
}
|
|
3845
3721
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3846
3722
|
name: entry.name,
|
|
3847
|
-
mtimeMs: statSync(
|
|
3723
|
+
mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
|
|
3848
3724
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3849
3725
|
for (const { name } of entries) {
|
|
3850
|
-
const candidate =
|
|
3851
|
-
if (
|
|
3726
|
+
const candidate = resolve12(distRoot, name, "bin", "rig");
|
|
3727
|
+
if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3852
3728
|
return candidate;
|
|
3853
3729
|
}
|
|
3854
3730
|
}
|
|
@@ -3860,7 +3736,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3860
3736
|
async function runDistDoctor(projectRoot) {
|
|
3861
3737
|
const bunPath = Bun.which("bun");
|
|
3862
3738
|
const rigPath = Bun.which("rig");
|
|
3863
|
-
const userBinDir =
|
|
3739
|
+
const userBinDir = resolve12(homedir3(), ".local/bin");
|
|
3864
3740
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3865
3741
|
let rigRunnable = false;
|
|
3866
3742
|
if (rigPath) {
|
|
@@ -3908,15 +3784,15 @@ async function executeDist(context, args) {
|
|
|
3908
3784
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3909
3785
|
let buildDir = null;
|
|
3910
3786
|
if (!source) {
|
|
3911
|
-
buildDir =
|
|
3787
|
+
buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3912
3788
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3913
|
-
source =
|
|
3789
|
+
source = resolve12(buildDir, "bin", "rig");
|
|
3914
3790
|
}
|
|
3915
|
-
if (!
|
|
3791
|
+
if (!existsSync7(source)) {
|
|
3916
3792
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3917
3793
|
}
|
|
3918
|
-
const installedPath =
|
|
3919
|
-
if (
|
|
3794
|
+
const installedPath = resolve12(installDir, "rig");
|
|
3795
|
+
if (existsSync7(installedPath)) {
|
|
3920
3796
|
unlinkSync(installedPath);
|
|
3921
3797
|
}
|
|
3922
3798
|
copyFileSync2(source, installedPath);
|
|
@@ -3958,22 +3834,22 @@ async function executeDist(context, args) {
|
|
|
3958
3834
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3959
3835
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3960
3836
|
const currentId = computeRuntimeImageId(fp);
|
|
3961
|
-
const imagesDir =
|
|
3837
|
+
const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3962
3838
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3963
3839
|
let pruned = 0;
|
|
3964
3840
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3965
3841
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3966
|
-
|
|
3842
|
+
rmSync3(resolve12(imagesDir, entry.name), { recursive: true, force: true });
|
|
3967
3843
|
pruned++;
|
|
3968
3844
|
}
|
|
3969
3845
|
}
|
|
3970
3846
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3971
3847
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3972
3848
|
}
|
|
3973
|
-
const imageDir =
|
|
3974
|
-
mkdirSync6(
|
|
3975
|
-
mkdirSync6(
|
|
3976
|
-
mkdirSync6(
|
|
3849
|
+
const imageDir = resolve12(imagesDir, currentId);
|
|
3850
|
+
mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
|
|
3851
|
+
mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
|
|
3852
|
+
mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
|
|
3977
3853
|
const hookNames = [
|
|
3978
3854
|
"scope-guard",
|
|
3979
3855
|
"import-guard",
|
|
@@ -3987,25 +3863,25 @@ async function executeDist(context, args) {
|
|
|
3987
3863
|
];
|
|
3988
3864
|
const targets = [];
|
|
3989
3865
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3990
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3991
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3866
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3867
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3992
3868
|
for (const hookName of hookNames) {
|
|
3993
3869
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3994
|
-
targets.push({ source: src, dest:
|
|
3995
|
-
targets.push({ source: src, dest:
|
|
3870
|
+
targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3871
|
+
targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3996
3872
|
}
|
|
3997
|
-
const pluginsDir =
|
|
3998
|
-
const binPluginsDir =
|
|
3999
|
-
const validatorsRoot =
|
|
4000
|
-
const binValidatorsDir =
|
|
3873
|
+
const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
|
|
3874
|
+
const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3875
|
+
const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3876
|
+
const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
4001
3877
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
4002
3878
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
4003
|
-
if (
|
|
3879
|
+
if (existsSync7(pluginsDir)) {
|
|
4004
3880
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
4005
3881
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
4006
3882
|
if (!m)
|
|
4007
3883
|
continue;
|
|
4008
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3884
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
4009
3885
|
}
|
|
4010
3886
|
}
|
|
4011
3887
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -4016,17 +3892,17 @@ async function executeDist(context, args) {
|
|
|
4016
3892
|
const isValidator = dest.includes("/bin/validators/");
|
|
4017
3893
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
4018
3894
|
}
|
|
4019
|
-
if (
|
|
3895
|
+
if (existsSync7(pluginsDir)) {
|
|
4020
3896
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
4021
3897
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
4022
3898
|
if (!m)
|
|
4023
3899
|
continue;
|
|
4024
3900
|
const pluginName = m[1];
|
|
4025
|
-
const imageBin =
|
|
3901
|
+
const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
|
|
4026
3902
|
if (!pluginName)
|
|
4027
3903
|
continue;
|
|
4028
|
-
const symlinkPath =
|
|
4029
|
-
if (
|
|
3904
|
+
const symlinkPath = resolve12(binPluginsDir, pluginName);
|
|
3905
|
+
if (existsSync7(imageBin)) {
|
|
4030
3906
|
try {
|
|
4031
3907
|
unlinkSync(symlinkPath);
|
|
4032
3908
|
} catch {}
|
|
@@ -4034,10 +3910,10 @@ async function executeDist(context, args) {
|
|
|
4034
3910
|
}
|
|
4035
3911
|
}
|
|
4036
3912
|
}
|
|
4037
|
-
if (
|
|
3913
|
+
if (existsSync7(validatorsRoot)) {
|
|
4038
3914
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
4039
3915
|
for (const category of categories) {
|
|
4040
|
-
const categoryDir =
|
|
3916
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
4041
3917
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
4042
3918
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
4043
3919
|
continue;
|
|
@@ -4045,9 +3921,9 @@ async function executeDist(context, args) {
|
|
|
4045
3921
|
if (!check || check === "index" || check === "shared")
|
|
4046
3922
|
continue;
|
|
4047
3923
|
const validatorName = `${category.name}-${check}`;
|
|
4048
|
-
const imageBin =
|
|
4049
|
-
const symlinkPath =
|
|
4050
|
-
if (
|
|
3924
|
+
const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
|
|
3925
|
+
const symlinkPath = resolve12(binValidatorsDir, validatorName);
|
|
3926
|
+
if (existsSync7(imageBin)) {
|
|
4051
3927
|
try {
|
|
4052
3928
|
unlinkSync(symlinkPath);
|
|
4053
3929
|
} catch {}
|
|
@@ -4056,18 +3932,18 @@ async function executeDist(context, args) {
|
|
|
4056
3932
|
}
|
|
4057
3933
|
}
|
|
4058
3934
|
}
|
|
4059
|
-
const agentsDir =
|
|
4060
|
-
if (
|
|
3935
|
+
const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3936
|
+
if (existsSync7(agentsDir)) {
|
|
4061
3937
|
let relinkCount = 0;
|
|
4062
3938
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
4063
3939
|
if (!agentEntry.isDirectory())
|
|
4064
3940
|
continue;
|
|
4065
|
-
const agentBinDir =
|
|
4066
|
-
if (!
|
|
3941
|
+
const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3942
|
+
if (!existsSync7(agentBinDir))
|
|
4067
3943
|
continue;
|
|
4068
3944
|
const walkDir = (dir) => {
|
|
4069
3945
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
4070
|
-
const fullPath =
|
|
3946
|
+
const fullPath = resolve12(dir, entry.name);
|
|
4071
3947
|
if (entry.isDirectory()) {
|
|
4072
3948
|
walkDir(fullPath);
|
|
4073
3949
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4101,7 +3977,7 @@ async function executeDist(context, args) {
|
|
|
4101
3977
|
|
|
4102
3978
|
// packages/cli/src/commands/inbox.ts
|
|
4103
3979
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4104
|
-
import { resolve as
|
|
3980
|
+
import { resolve as resolve13 } from "path";
|
|
4105
3981
|
import {
|
|
4106
3982
|
listAuthorityRuns,
|
|
4107
3983
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4118,7 +3994,7 @@ async function executeInbox(context, args) {
|
|
|
4118
3994
|
pending = task.rest;
|
|
4119
3995
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4120
3996
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4121
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
3997
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4122
3998
|
runId: entry.runId,
|
|
4123
3999
|
record
|
|
4124
4000
|
})));
|
|
@@ -4146,7 +4022,7 @@ async function executeInbox(context, args) {
|
|
|
4146
4022
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4147
4023
|
throw new CliError2("decision must be approve or reject.");
|
|
4148
4024
|
}
|
|
4149
|
-
const approvalsPath =
|
|
4025
|
+
const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4150
4026
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4151
4027
|
const resolvedAt = new Date().toISOString();
|
|
4152
4028
|
const next = approvals.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", decision: decision.value, note: note3.value ?? null, resolvedAt } : entry);
|
|
@@ -4163,7 +4039,7 @@ async function executeInbox(context, args) {
|
|
|
4163
4039
|
pending = task.rest;
|
|
4164
4040
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4165
4041
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4166
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
4042
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4167
4043
|
runId: entry.runId,
|
|
4168
4044
|
record
|
|
4169
4045
|
})));
|
|
@@ -4205,7 +4081,7 @@ async function executeInbox(context, args) {
|
|
|
4205
4081
|
const [key, ...restValue] = entry.split("=");
|
|
4206
4082
|
return [key, restValue.join("=")];
|
|
4207
4083
|
}));
|
|
4208
|
-
const requestsPath =
|
|
4084
|
+
const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4209
4085
|
const requests = readJsonlFile3(requestsPath);
|
|
4210
4086
|
const resolvedAt = new Date().toISOString();
|
|
4211
4087
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4218,13 +4094,190 @@ async function executeInbox(context, args) {
|
|
|
4218
4094
|
throw new CliError2(`Unknown inbox command: ${command}`);
|
|
4219
4095
|
}
|
|
4220
4096
|
}
|
|
4221
|
-
|
|
4222
|
-
// packages/cli/src/commands/init.ts
|
|
4223
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4224
|
-
import { spawnSync } from "child_process";
|
|
4225
|
-
import { resolve as resolve17 } from "path";
|
|
4226
|
-
import { buildRigInitConfigSource } from "@rig/core";
|
|
4227
|
-
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
4097
|
+
|
|
4098
|
+
// packages/cli/src/commands/init.ts
|
|
4099
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4100
|
+
import { spawnSync } from "child_process";
|
|
4101
|
+
import { resolve as resolve17 } from "path";
|
|
4102
|
+
import { buildRigInitConfigSource } from "@rig/core";
|
|
4103
|
+
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
4104
|
+
|
|
4105
|
+
// packages/cli/src/commands/_pi-install.ts
|
|
4106
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, rmSync as rmSync4 } from "fs";
|
|
4107
|
+
import { homedir as homedir4 } from "os";
|
|
4108
|
+
import { resolve as resolve14 } from "path";
|
|
4109
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
4110
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
4111
|
+
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
4112
|
+
export { default } from '@rig/pi-rig';
|
|
4113
|
+
`;
|
|
4114
|
+
async function defaultCommandRunner(command, options = {}) {
|
|
4115
|
+
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
4116
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
4117
|
+
new Response(proc.stdout).text(),
|
|
4118
|
+
new Response(proc.stderr).text(),
|
|
4119
|
+
proc.exited
|
|
4120
|
+
]);
|
|
4121
|
+
return { exitCode, stdout, stderr };
|
|
4122
|
+
}
|
|
4123
|
+
function resolvePiRigExtensionPath(homeDir) {
|
|
4124
|
+
return resolve14(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
4125
|
+
}
|
|
4126
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync8) {
|
|
4127
|
+
const localPackage = resolve14(projectRoot, "packages", "pi-rig");
|
|
4128
|
+
if (exists(resolve14(localPackage, "package.json")))
|
|
4129
|
+
return localPackage;
|
|
4130
|
+
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
4131
|
+
}
|
|
4132
|
+
function resolvePiHomeDir(inputHomeDir) {
|
|
4133
|
+
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir4();
|
|
4134
|
+
}
|
|
4135
|
+
function piListContainsPiRig(output) {
|
|
4136
|
+
return output.split(/\r?\n/).some((line) => {
|
|
4137
|
+
const normalized = line.trim();
|
|
4138
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
4139
|
+
});
|
|
4140
|
+
}
|
|
4141
|
+
async function safeRun(runner, command, options) {
|
|
4142
|
+
try {
|
|
4143
|
+
return await runner(command, options);
|
|
4144
|
+
} catch (error) {
|
|
4145
|
+
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
function splitInstallCommand(value) {
|
|
4149
|
+
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
4150
|
+
}
|
|
4151
|
+
async function ensurePiBinaryAvailable(input) {
|
|
4152
|
+
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
4153
|
+
if (current.exitCode === 0) {
|
|
4154
|
+
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
4155
|
+
if (updateCommand) {
|
|
4156
|
+
const parts2 = splitInstallCommand(updateCommand);
|
|
4157
|
+
if (parts2.length > 0)
|
|
4158
|
+
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
4159
|
+
}
|
|
4160
|
+
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
4161
|
+
}
|
|
4162
|
+
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
4163
|
+
const parts = splitInstallCommand(installCommand);
|
|
4164
|
+
if (parts.length === 0) {
|
|
4165
|
+
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
4166
|
+
}
|
|
4167
|
+
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
4168
|
+
if (install.exitCode !== 0) {
|
|
4169
|
+
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
4170
|
+
}
|
|
4171
|
+
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
4172
|
+
return {
|
|
4173
|
+
ok: next.exitCode === 0,
|
|
4174
|
+
installedOrUpdated: true,
|
|
4175
|
+
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
4176
|
+
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
4177
|
+
};
|
|
4178
|
+
}
|
|
4179
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync8) {
|
|
4180
|
+
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
4181
|
+
const indexPath = resolve14(extensionPath, "index.ts");
|
|
4182
|
+
if (!exists(indexPath))
|
|
4183
|
+
return;
|
|
4184
|
+
try {
|
|
4185
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
4186
|
+
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
4187
|
+
rmSync4(extensionPath, { recursive: true, force: true });
|
|
4188
|
+
}
|
|
4189
|
+
} catch {}
|
|
4190
|
+
}
|
|
4191
|
+
async function checkPiRigInstall(input = {}) {
|
|
4192
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
4193
|
+
const extensionPath = resolvePiRigExtensionPath(home);
|
|
4194
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
4195
|
+
return {
|
|
4196
|
+
extensionPath,
|
|
4197
|
+
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
4198
|
+
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
const exists = input.exists ?? existsSync8;
|
|
4202
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
4203
|
+
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
4204
|
+
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
4205
|
+
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
4206
|
+
${piListResult.stderr}`);
|
|
4207
|
+
const legacyBridge = exists(resolve14(extensionPath, "index.ts"));
|
|
4208
|
+
const hasPiRig = listedPiRig;
|
|
4209
|
+
return {
|
|
4210
|
+
extensionPath,
|
|
4211
|
+
pi: {
|
|
4212
|
+
ok: piResult.exitCode === 0,
|
|
4213
|
+
label: "pi",
|
|
4214
|
+
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
4215
|
+
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
4216
|
+
},
|
|
4217
|
+
piRig: {
|
|
4218
|
+
ok: hasPiRig,
|
|
4219
|
+
label: "pi-rig global extension",
|
|
4220
|
+
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
4221
|
+
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
4222
|
+
}
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
async function ensurePiRigInstalled(input) {
|
|
4226
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
4227
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
4228
|
+
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
4229
|
+
return { ...status2, installedPath: status2.extensionPath };
|
|
4230
|
+
}
|
|
4231
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
4232
|
+
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
4233
|
+
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
4234
|
+
extensionPath: resolvePiRigExtensionPath(home),
|
|
4235
|
+
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
4236
|
+
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
4237
|
+
};
|
|
4238
|
+
if (!piAvailable.ok) {
|
|
4239
|
+
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
4240
|
+
}
|
|
4241
|
+
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
4242
|
+
removeManagedLegacyPiRigBridge(home);
|
|
4243
|
+
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
4244
|
+
if (install.exitCode !== 0) {
|
|
4245
|
+
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
4246
|
+
}
|
|
4247
|
+
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
4248
|
+
return { ...next, installedPath: packageSource };
|
|
4249
|
+
}
|
|
4250
|
+
async function ensureRemotePiRigInstalled(input) {
|
|
4251
|
+
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
4252
|
+
method: "POST",
|
|
4253
|
+
headers: { "content-type": "application/json" },
|
|
4254
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
4255
|
+
});
|
|
4256
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4257
|
+
const piOk = record.piOk === true || record.ok === true;
|
|
4258
|
+
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
4259
|
+
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
4260
|
+
return {
|
|
4261
|
+
remote: true,
|
|
4262
|
+
extensionPath,
|
|
4263
|
+
pi: {
|
|
4264
|
+
ok: piOk,
|
|
4265
|
+
label: "pi",
|
|
4266
|
+
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
4267
|
+
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
4268
|
+
},
|
|
4269
|
+
piRig: {
|
|
4270
|
+
ok: piRigOk,
|
|
4271
|
+
label: "pi-rig global extension",
|
|
4272
|
+
detail: extensionPath,
|
|
4273
|
+
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
4274
|
+
}
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
4277
|
+
async function buildPiSetupChecks(input = {}) {
|
|
4278
|
+
const status = await checkPiRigInstall(input);
|
|
4279
|
+
return [status.pi, status.piRig];
|
|
4280
|
+
}
|
|
4228
4281
|
|
|
4229
4282
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4230
4283
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
@@ -6592,8 +6645,477 @@ async function promptForTaskSelection(question) {
|
|
|
6592
6645
|
}
|
|
6593
6646
|
}
|
|
6594
6647
|
|
|
6595
|
-
// packages/cli/src/commands/
|
|
6648
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
6649
|
+
import { mkdtempSync, rmSync as rmSync5 } from "fs";
|
|
6650
|
+
import { tmpdir } from "os";
|
|
6651
|
+
import { join as join2 } from "path";
|
|
6652
|
+
import { main as runPiMain } from "@earendil-works/pi-coding-agent";
|
|
6653
|
+
|
|
6654
|
+
// packages/cli/src/commands/_pi-worker-bridge-extension.ts
|
|
6596
6655
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6656
|
+
var MAX_TRANSCRIPT_LINES = 120;
|
|
6657
|
+
function recordOf(value) {
|
|
6658
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6659
|
+
}
|
|
6660
|
+
function asText(value) {
|
|
6661
|
+
if (typeof value === "string")
|
|
6662
|
+
return value;
|
|
6663
|
+
if (value === null || value === undefined)
|
|
6664
|
+
return "";
|
|
6665
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
6666
|
+
return String(value);
|
|
6667
|
+
try {
|
|
6668
|
+
return JSON.stringify(value);
|
|
6669
|
+
} catch {
|
|
6670
|
+
return String(value);
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6673
|
+
function textFromContent(content) {
|
|
6674
|
+
if (typeof content === "string")
|
|
6675
|
+
return content;
|
|
6676
|
+
if (!Array.isArray(content))
|
|
6677
|
+
return asText(content);
|
|
6678
|
+
return content.flatMap((part) => {
|
|
6679
|
+
const item = recordOf(part);
|
|
6680
|
+
if (!item)
|
|
6681
|
+
return [];
|
|
6682
|
+
if (typeof item.text === "string")
|
|
6683
|
+
return [item.text];
|
|
6684
|
+
if (typeof item.content === "string")
|
|
6685
|
+
return [item.content];
|
|
6686
|
+
if (item.type === "toolCall")
|
|
6687
|
+
return [`\u23FA ${String(item.name ?? "tool")} ${asText(item.arguments ?? "")}`.trim()];
|
|
6688
|
+
if (item.type === "toolResult")
|
|
6689
|
+
return [`\u21B3 ${asText(item.content ?? item.result ?? "")}`.trim()];
|
|
6690
|
+
return [];
|
|
6691
|
+
}).join(`
|
|
6692
|
+
`);
|
|
6693
|
+
}
|
|
6694
|
+
function appendTranscript(state, label, text2) {
|
|
6695
|
+
const trimmed = text2.trimEnd();
|
|
6696
|
+
if (!trimmed)
|
|
6697
|
+
return;
|
|
6698
|
+
const lines = trimmed.split(/\r?\n/);
|
|
6699
|
+
state.transcript.push(`${label}: ${lines[0] ?? ""}`);
|
|
6700
|
+
for (const line of lines.slice(1))
|
|
6701
|
+
state.transcript.push(` ${line}`);
|
|
6702
|
+
if (state.transcript.length > MAX_TRANSCRIPT_LINES) {
|
|
6703
|
+
state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6706
|
+
function parseExtensionUiRequest(value) {
|
|
6707
|
+
const request = recordOf(value) ?? {};
|
|
6708
|
+
const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
|
|
6709
|
+
const method = String(request.method ?? request.type ?? "input");
|
|
6710
|
+
const prompt = asText(request.prompt ?? request.message ?? request.title ?? method);
|
|
6711
|
+
const rawOptions = Array.isArray(request.options) ? request.options : Array.isArray(request.items) ? request.items : [];
|
|
6712
|
+
const options = rawOptions.map((option) => {
|
|
6713
|
+
const record = recordOf(option);
|
|
6714
|
+
return record ? asText(record.label ?? record.value ?? record.name ?? option) : asText(option);
|
|
6715
|
+
}).filter(Boolean);
|
|
6716
|
+
return { requestId, method, prompt, options };
|
|
6717
|
+
}
|
|
6718
|
+
function renderBridgeWidget(state) {
|
|
6719
|
+
const statusParts = [
|
|
6720
|
+
state.wsConnected ? "live WS" : "WS pending",
|
|
6721
|
+
state.status,
|
|
6722
|
+
state.model,
|
|
6723
|
+
state.cwd
|
|
6724
|
+
].filter(Boolean);
|
|
6725
|
+
const lines = [`Worker Pi daemon bridge \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
6726
|
+
if (state.activity)
|
|
6727
|
+
lines.push(state.activity);
|
|
6728
|
+
if (state.commands.length > 0) {
|
|
6729
|
+
lines.push(`Worker commands: ${state.commands.slice(0, 10).join(", ")}${state.commands.length > 10 ? ", \u2026" : ""}`);
|
|
6730
|
+
}
|
|
6731
|
+
lines.push("");
|
|
6732
|
+
if (state.transcript.length > 0) {
|
|
6733
|
+
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
6734
|
+
} else {
|
|
6735
|
+
lines.push("Waiting for worker Pi daemon transcript\u2026");
|
|
6736
|
+
}
|
|
6737
|
+
if (state.pendingUi) {
|
|
6738
|
+
lines.push("");
|
|
6739
|
+
lines.push(`Extension UI request \xB7 ${state.pendingUi.method}`);
|
|
6740
|
+
lines.push(state.pendingUi.prompt);
|
|
6741
|
+
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
6742
|
+
lines.push("Reply in the Pi editor. /cancel cancels this request.");
|
|
6743
|
+
}
|
|
6744
|
+
return lines;
|
|
6745
|
+
}
|
|
6746
|
+
function updatePiUi(ctx, state) {
|
|
6747
|
+
ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
|
|
6748
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
|
|
6749
|
+
ctx.ui.setWorkingVisible(false);
|
|
6750
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
6751
|
+
}
|
|
6752
|
+
function applyStatus(state, payload) {
|
|
6753
|
+
const status = recordOf(payload.status) ?? payload;
|
|
6754
|
+
state.streaming = status.isStreaming === true || status.isCompacting === true || status.isBashRunning === true;
|
|
6755
|
+
state.cwd = typeof status.cwd === "string" ? status.cwd : state.cwd;
|
|
6756
|
+
state.model = typeof status.model === "string" ? status.model : state.model;
|
|
6757
|
+
const pending = typeof status.pendingMessageCount === "number" ? status.pendingMessageCount : 0;
|
|
6758
|
+
state.status = `${state.streaming ? "streaming" : "idle"}${pending ? ` \xB7 ${pending} queued` : ""}`;
|
|
6759
|
+
}
|
|
6760
|
+
function applyMessage(state, message2) {
|
|
6761
|
+
const record = recordOf(message2);
|
|
6762
|
+
if (!record)
|
|
6763
|
+
return;
|
|
6764
|
+
const role = String(record.role ?? "system");
|
|
6765
|
+
const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
|
|
6766
|
+
appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
|
|
6767
|
+
}
|
|
6768
|
+
function applyPiEvent(state, eventValue) {
|
|
6769
|
+
const event = recordOf(eventValue);
|
|
6770
|
+
if (!event)
|
|
6771
|
+
return;
|
|
6772
|
+
const type = String(event.type ?? "event");
|
|
6773
|
+
if (type === "agent_start") {
|
|
6774
|
+
state.streaming = true;
|
|
6775
|
+
state.status = "streaming";
|
|
6776
|
+
return;
|
|
6777
|
+
}
|
|
6778
|
+
if (type === "agent_end") {
|
|
6779
|
+
state.streaming = false;
|
|
6780
|
+
state.status = "idle";
|
|
6781
|
+
appendTranscript(state, "System", "Agent turn complete.");
|
|
6782
|
+
return;
|
|
6783
|
+
}
|
|
6784
|
+
if (type === "message_start" || type === "message_end" || type === "turn_end") {
|
|
6785
|
+
applyMessage(state, event.message);
|
|
6786
|
+
return;
|
|
6787
|
+
}
|
|
6788
|
+
if (type === "message_update") {
|
|
6789
|
+
const assistantEvent = recordOf(event.assistantMessageEvent);
|
|
6790
|
+
const delta = typeof assistantEvent?.delta === "string" ? assistantEvent.delta : typeof assistantEvent?.text === "string" ? assistantEvent.text : "";
|
|
6791
|
+
if (delta)
|
|
6792
|
+
appendTranscript(state, assistantEvent?.type === "thinking_delta" ? "Thinking" : "Pi", delta);
|
|
6793
|
+
return;
|
|
6794
|
+
}
|
|
6795
|
+
if (type === "tool_execution_start") {
|
|
6796
|
+
appendTranscript(state, "Tool", `${String(event.toolName ?? "tool")} ${asText(event.args ?? "")}`.trim());
|
|
6797
|
+
return;
|
|
6798
|
+
}
|
|
6799
|
+
if (type === "tool_execution_update") {
|
|
6800
|
+
appendTranscript(state, "Tool", asText(event.partialResult ?? ""));
|
|
6801
|
+
return;
|
|
6802
|
+
}
|
|
6803
|
+
if (type === "tool_execution_end") {
|
|
6804
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
|
|
6805
|
+
return;
|
|
6806
|
+
}
|
|
6807
|
+
if (type === "queue_update") {
|
|
6808
|
+
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
6809
|
+
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
6810
|
+
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
6811
|
+
}
|
|
6812
|
+
}
|
|
6813
|
+
function applyUiEvent(state, value) {
|
|
6814
|
+
const event = recordOf(value);
|
|
6815
|
+
if (!event)
|
|
6816
|
+
return;
|
|
6817
|
+
const type = String(event.type ?? "ui");
|
|
6818
|
+
if (type === "shell.start")
|
|
6819
|
+
appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
|
|
6820
|
+
else if (type === "shell.chunk")
|
|
6821
|
+
appendTranscript(state, "Tool", asText(event.chunk));
|
|
6822
|
+
else if (type === "shell.end")
|
|
6823
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.output ?? `exit ${String(event.exitCode ?? "")}`));
|
|
6824
|
+
else
|
|
6825
|
+
appendTranscript(state, "System", `${type}: ${asText(event)}`);
|
|
6826
|
+
}
|
|
6827
|
+
function applyEnvelope(state, envelopeValue) {
|
|
6828
|
+
const envelope = recordOf(envelopeValue);
|
|
6829
|
+
if (!envelope)
|
|
6830
|
+
return;
|
|
6831
|
+
const type = String(envelope.type ?? "");
|
|
6832
|
+
if (type === "ready") {
|
|
6833
|
+
const metadata = recordOf(envelope.metadata);
|
|
6834
|
+
state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
|
|
6835
|
+
state.status = "worker Pi daemon ready";
|
|
6836
|
+
appendTranscript(state, "System", "Connected to worker Pi daemon.");
|
|
6837
|
+
} else if (type === "status.update") {
|
|
6838
|
+
applyStatus(state, envelope);
|
|
6839
|
+
} else if (type === "activity.update") {
|
|
6840
|
+
const activity = recordOf(envelope.activity);
|
|
6841
|
+
state.activity = [activity?.label, activity?.detail].map(asText).filter(Boolean).join(" \u2014 ");
|
|
6842
|
+
} else if (type === "extension_ui_request") {
|
|
6843
|
+
state.pendingUi = parseExtensionUiRequest(envelope.request);
|
|
6844
|
+
appendTranscript(state, "System", `Extension UI request: ${state.pendingUi.prompt}`);
|
|
6845
|
+
} else if (type === "pi.ui_event") {
|
|
6846
|
+
applyUiEvent(state, envelope.event);
|
|
6847
|
+
} else if (type === "pi.event") {
|
|
6848
|
+
applyPiEvent(state, envelope.event);
|
|
6849
|
+
} else if (type === "error") {
|
|
6850
|
+
appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
6853
|
+
async function waitForWorkerReady(options, ctx, state) {
|
|
6854
|
+
while (true) {
|
|
6855
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => ({
|
|
6856
|
+
ready: false,
|
|
6857
|
+
status: error instanceof Error ? error.message : String(error),
|
|
6858
|
+
retryAfterMs: 1000
|
|
6859
|
+
}));
|
|
6860
|
+
if (session.ready === false) {
|
|
6861
|
+
const status = String(session.status ?? "starting");
|
|
6862
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
6863
|
+
updatePiUi(ctx, state);
|
|
6864
|
+
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
6865
|
+
appendTranscript(state, "Error", `Run ended before worker Pi daemon became ready: ${status}`);
|
|
6866
|
+
return false;
|
|
6867
|
+
}
|
|
6868
|
+
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
6869
|
+
continue;
|
|
6870
|
+
}
|
|
6871
|
+
const sessionRecord = recordOf(session) ?? {};
|
|
6872
|
+
applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
6873
|
+
updatePiUi(ctx, state);
|
|
6874
|
+
return true;
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6877
|
+
function parseWsPayload(message2) {
|
|
6878
|
+
if (typeof message2.data === "string")
|
|
6879
|
+
return JSON.parse(message2.data);
|
|
6880
|
+
return JSON.parse(Buffer.from(message2.data).toString("utf8"));
|
|
6881
|
+
}
|
|
6882
|
+
async function connectWorkerStream(options, ctx, state) {
|
|
6883
|
+
const ready = await waitForWorkerReady(options, ctx, state);
|
|
6884
|
+
if (!ready)
|
|
6885
|
+
return;
|
|
6886
|
+
let catchupDone = false;
|
|
6887
|
+
const buffered = [];
|
|
6888
|
+
const wsUrl = await buildRunPiEventsWebSocketUrl(options.context, options.runId);
|
|
6889
|
+
const socket = new WebSocket(wsUrl);
|
|
6890
|
+
const closePromise = new Promise((resolve20) => {
|
|
6891
|
+
socket.onopen = () => {
|
|
6892
|
+
state.wsConnected = true;
|
|
6893
|
+
state.status = "live worker Pi WebSocket connected";
|
|
6894
|
+
updatePiUi(ctx, state);
|
|
6895
|
+
};
|
|
6896
|
+
socket.onmessage = (message2) => {
|
|
6897
|
+
try {
|
|
6898
|
+
const payload = parseWsPayload(message2);
|
|
6899
|
+
if (!catchupDone)
|
|
6900
|
+
buffered.push(payload);
|
|
6901
|
+
else {
|
|
6902
|
+
applyEnvelope(state, payload);
|
|
6903
|
+
updatePiUi(ctx, state);
|
|
6904
|
+
}
|
|
6905
|
+
} catch (error) {
|
|
6906
|
+
appendTranscript(state, "Error", `Unparseable worker Pi event: ${error instanceof Error ? error.message : String(error)}`);
|
|
6907
|
+
updatePiUi(ctx, state);
|
|
6908
|
+
}
|
|
6909
|
+
};
|
|
6910
|
+
socket.onerror = () => socket.close();
|
|
6911
|
+
socket.onclose = () => {
|
|
6912
|
+
state.wsConnected = false;
|
|
6913
|
+
state.status = "worker Pi WebSocket disconnected";
|
|
6914
|
+
updatePiUi(ctx, state);
|
|
6915
|
+
resolve20();
|
|
6916
|
+
};
|
|
6917
|
+
});
|
|
6918
|
+
try {
|
|
6919
|
+
const [messagesPayload, statusPayload, commandsPayload] = await Promise.all([
|
|
6920
|
+
getRunPiMessagesViaServer(options.context, options.runId),
|
|
6921
|
+
getRunPiStatusViaServer(options.context, options.runId),
|
|
6922
|
+
getRunPiCommandsViaServer(options.context, options.runId)
|
|
6923
|
+
]);
|
|
6924
|
+
const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
|
|
6925
|
+
for (const message2 of messages)
|
|
6926
|
+
applyMessage(state, message2);
|
|
6927
|
+
applyStatus(state, statusPayload);
|
|
6928
|
+
const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
|
|
6929
|
+
state.commands = commands.flatMap((command) => {
|
|
6930
|
+
const record = recordOf(command);
|
|
6931
|
+
return typeof record?.name === "string" ? [`/${record.name}`] : [];
|
|
6932
|
+
});
|
|
6933
|
+
catchupDone = true;
|
|
6934
|
+
for (const payload of buffered.splice(0))
|
|
6935
|
+
applyEnvelope(state, payload);
|
|
6936
|
+
updatePiUi(ctx, state);
|
|
6937
|
+
} catch (error) {
|
|
6938
|
+
appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6939
|
+
catchupDone = true;
|
|
6940
|
+
updatePiUi(ctx, state);
|
|
6941
|
+
}
|
|
6942
|
+
await closePromise;
|
|
6943
|
+
}
|
|
6944
|
+
async function answerPendingUi(options, state, line) {
|
|
6945
|
+
const pending = state.pendingUi;
|
|
6946
|
+
if (!pending)
|
|
6947
|
+
return false;
|
|
6948
|
+
if (line === "/cancel") {
|
|
6949
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { cancelled: true });
|
|
6950
|
+
} else if (pending.method === "confirm") {
|
|
6951
|
+
const confirmed = /^(y|yes|true|1)$/i.test(line);
|
|
6952
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: confirmed, confirmed });
|
|
6953
|
+
} else if (pending.options.length > 0 && /^\d+$/.test(line)) {
|
|
6954
|
+
const selected = pending.options[Math.max(0, Number(line) - 1)] ?? line;
|
|
6955
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: selected });
|
|
6956
|
+
} else {
|
|
6957
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: line });
|
|
6958
|
+
}
|
|
6959
|
+
appendTranscript(state, "System", `Responded to extension UI request ${pending.requestId}.`);
|
|
6960
|
+
state.pendingUi = null;
|
|
6961
|
+
return true;
|
|
6962
|
+
}
|
|
6963
|
+
async function routeInput(options, ctx, state, line) {
|
|
6964
|
+
const text2 = line.trim();
|
|
6965
|
+
if (!text2)
|
|
6966
|
+
return;
|
|
6967
|
+
if (await answerPendingUi(options, state, text2)) {
|
|
6968
|
+
updatePiUi(ctx, state);
|
|
6969
|
+
return;
|
|
6970
|
+
}
|
|
6971
|
+
if (text2 === "/detach" || text2 === "/quit" || text2 === "/q") {
|
|
6972
|
+
appendTranscript(state, "System", "Detached locally; worker Pi daemon continues.");
|
|
6973
|
+
updatePiUi(ctx, state);
|
|
6974
|
+
ctx.shutdown();
|
|
6975
|
+
return;
|
|
6976
|
+
}
|
|
6977
|
+
if (text2 === "/stop") {
|
|
6978
|
+
await abortRunPiViaServer(options.context, options.runId);
|
|
6979
|
+
appendTranscript(state, "System", "Stop requested for worker Pi daemon.");
|
|
6980
|
+
updatePiUi(ctx, state);
|
|
6981
|
+
ctx.shutdown();
|
|
6982
|
+
return;
|
|
6983
|
+
}
|
|
6984
|
+
if (text2.startsWith("!")) {
|
|
6985
|
+
appendTranscript(state, "You", text2);
|
|
6986
|
+
await sendRunPiShellViaServer(options.context, options.runId, text2);
|
|
6987
|
+
} else if (text2.startsWith("/")) {
|
|
6988
|
+
appendTranscript(state, "You", text2);
|
|
6989
|
+
const result = await runRunPiCommandViaServer(options.context, options.runId, text2);
|
|
6990
|
+
const message2 = typeof result.message === "string" ? result.message : "worker command accepted";
|
|
6991
|
+
appendTranscript(state, "System", message2);
|
|
6992
|
+
} else {
|
|
6993
|
+
appendTranscript(state, "You", text2);
|
|
6994
|
+
await sendRunPiPromptViaServer(options.context, options.runId, text2, state.streaming ? "steer" : undefined);
|
|
6995
|
+
}
|
|
6996
|
+
updatePiUi(ctx, state);
|
|
6997
|
+
}
|
|
6998
|
+
function createRigWorkerPiBridgeExtension(options) {
|
|
6999
|
+
return (pi) => {
|
|
7000
|
+
const state = {
|
|
7001
|
+
transcript: [],
|
|
7002
|
+
status: "starting worker Pi daemon bridge",
|
|
7003
|
+
activity: "",
|
|
7004
|
+
cwd: "",
|
|
7005
|
+
model: "",
|
|
7006
|
+
commands: [],
|
|
7007
|
+
streaming: false,
|
|
7008
|
+
pendingUi: null,
|
|
7009
|
+
wsConnected: false
|
|
7010
|
+
};
|
|
7011
|
+
if (options.initialMessageSent)
|
|
7012
|
+
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
7013
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
7014
|
+
updatePiUi(ctx, state);
|
|
7015
|
+
ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
|
|
7016
|
+
ctx.ui.onTerminalInput((data) => {
|
|
7017
|
+
if (data.includes("\x04")) {
|
|
7018
|
+
ctx.shutdown();
|
|
7019
|
+
return { consume: true };
|
|
7020
|
+
}
|
|
7021
|
+
if (!data.includes("\r") && !data.includes(`
|
|
7022
|
+
`))
|
|
7023
|
+
return;
|
|
7024
|
+
const inlineText = data.replace(/[\r\n]+/g, "").trim();
|
|
7025
|
+
const editorText = ctx.ui.getEditorText().trim();
|
|
7026
|
+
const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
|
|
7027
|
+
if (!text2)
|
|
7028
|
+
return;
|
|
7029
|
+
ctx.ui.setEditorText("");
|
|
7030
|
+
routeInput(options, ctx, state, text2).catch((error) => {
|
|
7031
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
7032
|
+
updatePiUi(ctx, state);
|
|
7033
|
+
});
|
|
7034
|
+
return { consume: true };
|
|
7035
|
+
});
|
|
7036
|
+
connectWorkerStream(options, ctx, state).catch((error) => {
|
|
7037
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
7038
|
+
updatePiUi(ctx, state);
|
|
7039
|
+
});
|
|
7040
|
+
});
|
|
7041
|
+
pi.on("session_shutdown", () => {});
|
|
7042
|
+
};
|
|
7043
|
+
}
|
|
7044
|
+
|
|
7045
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
7046
|
+
function setTemporaryEnv(updates) {
|
|
7047
|
+
const previous = new Map;
|
|
7048
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
7049
|
+
previous.set(key, process.env[key]);
|
|
7050
|
+
process.env[key] = value;
|
|
7051
|
+
}
|
|
7052
|
+
return () => {
|
|
7053
|
+
for (const [key, value] of previous) {
|
|
7054
|
+
if (value === undefined)
|
|
7055
|
+
delete process.env[key];
|
|
7056
|
+
else
|
|
7057
|
+
process.env[key] = value;
|
|
7058
|
+
}
|
|
7059
|
+
};
|
|
7060
|
+
}
|
|
7061
|
+
async function attachRunBundledPiFrontend(context, input) {
|
|
7062
|
+
const tempRoot = mkdtempSync(join2(tmpdir(), "rig-pi-frontend-"));
|
|
7063
|
+
const cwd = join2(tempRoot, "workspace");
|
|
7064
|
+
const agentDir = join2(tempRoot, "agent");
|
|
7065
|
+
const sessionDir = join2(tempRoot, "sessions");
|
|
7066
|
+
const previousCwd = process.cwd();
|
|
7067
|
+
const restoreEnv = setTemporaryEnv({
|
|
7068
|
+
PI_CODING_AGENT_DIR: agentDir,
|
|
7069
|
+
PI_CODING_AGENT_SESSION_DIR: sessionDir,
|
|
7070
|
+
PI_OFFLINE: "1",
|
|
7071
|
+
PI_SKIP_VERSION_CHECK: "1"
|
|
7072
|
+
});
|
|
7073
|
+
let detached = false;
|
|
7074
|
+
try {
|
|
7075
|
+
await Bun.$`mkdir -p ${cwd} ${agentDir} ${sessionDir}`.quiet();
|
|
7076
|
+
process.chdir(cwd);
|
|
7077
|
+
await runPiMain([
|
|
7078
|
+
"--offline",
|
|
7079
|
+
"--no-session",
|
|
7080
|
+
"--no-tools",
|
|
7081
|
+
"--no-builtin-tools",
|
|
7082
|
+
"--no-skills",
|
|
7083
|
+
"--no-prompt-templates",
|
|
7084
|
+
"--no-themes",
|
|
7085
|
+
"--no-context-files",
|
|
7086
|
+
"--no-approve"
|
|
7087
|
+
], {
|
|
7088
|
+
extensionFactories: [
|
|
7089
|
+
createRigWorkerPiBridgeExtension({
|
|
7090
|
+
context,
|
|
7091
|
+
runId: input.runId,
|
|
7092
|
+
initialMessageSent: input.steered === true
|
|
7093
|
+
})
|
|
7094
|
+
]
|
|
7095
|
+
});
|
|
7096
|
+
detached = true;
|
|
7097
|
+
} finally {
|
|
7098
|
+
process.chdir(previousCwd);
|
|
7099
|
+
restoreEnv();
|
|
7100
|
+
rmSync5(tempRoot, { recursive: true, force: true });
|
|
7101
|
+
}
|
|
7102
|
+
let run = { runId: input.runId, status: "unknown" };
|
|
7103
|
+
try {
|
|
7104
|
+
run = await getRunDetailsViaServer(context, input.runId);
|
|
7105
|
+
} catch {}
|
|
7106
|
+
return {
|
|
7107
|
+
run,
|
|
7108
|
+
logs: [],
|
|
7109
|
+
timeline: [],
|
|
7110
|
+
timelineCursor: null,
|
|
7111
|
+
steered: input.steered === true,
|
|
7112
|
+
detached,
|
|
7113
|
+
rendered: "actual bundled Pi frontend hosted Rig worker Pi bridge extension"
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7117
|
+
// packages/cli/src/commands/_operator-view.ts
|
|
7118
|
+
var TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6597
7119
|
function runStatusFromPayload(payload) {
|
|
6598
7120
|
const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
|
|
6599
7121
|
return String(run.status ?? "unknown").toLowerCase();
|
|
@@ -6635,9 +7157,15 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
6635
7157
|
async function attachRunOperatorView(context, input) {
|
|
6636
7158
|
let steered = false;
|
|
6637
7159
|
if (input.message?.trim()) {
|
|
6638
|
-
await steerRunViaServer(context, input.runId, input.message.trim());
|
|
7160
|
+
await sendRunPiPromptViaServer(context, input.runId, input.message.trim(), "steer").catch(() => steerRunViaServer(context, input.runId, input.message.trim()));
|
|
6639
7161
|
steered = true;
|
|
6640
7162
|
}
|
|
7163
|
+
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
7164
|
+
return attachRunBundledPiFrontend(context, {
|
|
7165
|
+
runId: input.runId,
|
|
7166
|
+
steered
|
|
7167
|
+
});
|
|
7168
|
+
}
|
|
6641
7169
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
6642
7170
|
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
6643
7171
|
if (context.outputMode === "text") {
|
|
@@ -6645,7 +7173,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6645
7173
|
surface.renderTimeline(snapshot.timeline);
|
|
6646
7174
|
surface.renderLogs(snapshot.logs);
|
|
6647
7175
|
if (steered)
|
|
6648
|
-
surface.info("
|
|
7176
|
+
surface.info("Message submitted to worker Pi.");
|
|
6649
7177
|
}
|
|
6650
7178
|
let detached = false;
|
|
6651
7179
|
let commandInput = null;
|
|
@@ -6664,7 +7192,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6664
7192
|
}
|
|
6665
7193
|
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
|
|
6666
7194
|
let timelineCursor = snapshot.timelineCursor;
|
|
6667
|
-
while (!detached && !
|
|
7195
|
+
while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
|
|
6668
7196
|
await Bun.sleep(pollMs);
|
|
6669
7197
|
snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6670
7198
|
timelineCursor = snapshot.timelineCursor;
|
|
@@ -6778,92 +7306,6 @@ function formatSubmittedRun(input) {
|
|
|
6778
7306
|
`);
|
|
6779
7307
|
}
|
|
6780
7308
|
|
|
6781
|
-
// packages/cli/src/commands/_pi-session.ts
|
|
6782
|
-
import { spawn as spawn2 } from "child_process";
|
|
6783
|
-
function buildPiRigSessionEnv(input) {
|
|
6784
|
-
return {
|
|
6785
|
-
RIG_PROJECT_ROOT: input.projectRoot,
|
|
6786
|
-
PROJECT_RIG_ROOT: input.projectRoot,
|
|
6787
|
-
RIG_RUN_ID: input.runId,
|
|
6788
|
-
RIG_SERVER_RUN_ID: input.runId,
|
|
6789
|
-
RIG_RUNTIME_ADAPTER: "pi",
|
|
6790
|
-
RIG_SERVER_URL: input.serverUrl,
|
|
6791
|
-
RIG_SERVER_BASE_URL: input.serverUrl,
|
|
6792
|
-
RIG_STEERING_POLL_MS: process.env.RIG_STEERING_POLL_MS?.trim() || "1000",
|
|
6793
|
-
RIG_PI_OPERATOR_SESSION: "1",
|
|
6794
|
-
...input.taskId ? { RIG_TASK_ID: input.taskId } : {},
|
|
6795
|
-
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {}
|
|
6796
|
-
};
|
|
6797
|
-
}
|
|
6798
|
-
function shellBinary(name) {
|
|
6799
|
-
const explicit = process.env.RIG_PI_BINARY?.trim();
|
|
6800
|
-
if (explicit)
|
|
6801
|
-
return explicit;
|
|
6802
|
-
return Bun.which(name) || name;
|
|
6803
|
-
}
|
|
6804
|
-
function buildPiRigSessionCommand(input) {
|
|
6805
|
-
const configuredExtension = input.extensionSource ?? process.env.RIG_PI_RIG_EXTENSION_SOURCE?.trim();
|
|
6806
|
-
const extensionSource = configuredExtension && configuredExtension.length > 0 ? configuredExtension : resolvePiRigPackageSource(input.projectRoot);
|
|
6807
|
-
const initialCommand = `/rig attach ${input.runId}`;
|
|
6808
|
-
return [
|
|
6809
|
-
shellBinary("pi"),
|
|
6810
|
-
"--no-extensions",
|
|
6811
|
-
"--extension",
|
|
6812
|
-
extensionSource,
|
|
6813
|
-
initialCommand
|
|
6814
|
-
];
|
|
6815
|
-
}
|
|
6816
|
-
async function launchPiRigSession(context, input) {
|
|
6817
|
-
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
6818
|
-
return { launched: false, exitCode: null, command: [] };
|
|
6819
|
-
}
|
|
6820
|
-
if (process.env.RIG_DISABLE_PI_LAUNCH === "1") {
|
|
6821
|
-
return { launched: false, exitCode: null, command: [] };
|
|
6822
|
-
}
|
|
6823
|
-
const server = await ensureServerForCli(context.projectRoot);
|
|
6824
|
-
const command = buildPiRigSessionCommand({ ...input, projectRoot: context.projectRoot });
|
|
6825
|
-
const env = {
|
|
6826
|
-
...process.env,
|
|
6827
|
-
...buildPiRigSessionEnv({
|
|
6828
|
-
projectRoot: context.projectRoot,
|
|
6829
|
-
runId: input.runId,
|
|
6830
|
-
taskId: input.taskId,
|
|
6831
|
-
serverUrl: server.baseUrl,
|
|
6832
|
-
authToken: server.authToken
|
|
6833
|
-
})
|
|
6834
|
-
};
|
|
6835
|
-
process.stdout.write(`Launching Pi for Rig run ${input.runId}\u2026
|
|
6836
|
-
`);
|
|
6837
|
-
process.stdout.write(`Pi command: ${formatCommand(command)}
|
|
6838
|
-
`);
|
|
6839
|
-
const launchedAt = Date.now();
|
|
6840
|
-
const child = spawn2(command[0], command.slice(1), {
|
|
6841
|
-
cwd: context.projectRoot,
|
|
6842
|
-
env,
|
|
6843
|
-
stdio: "inherit"
|
|
6844
|
-
});
|
|
6845
|
-
const launchError = await new Promise((resolve20) => {
|
|
6846
|
-
child.once("error", (error) => {
|
|
6847
|
-
resolve20({ error: error.message });
|
|
6848
|
-
});
|
|
6849
|
-
child.once("close", (code) => resolve20({ code }));
|
|
6850
|
-
});
|
|
6851
|
-
if ("error" in launchError) {
|
|
6852
|
-
process.stderr.write(`Failed to launch Pi; falling back to Rig attach view: ${launchError.error}
|
|
6853
|
-
`);
|
|
6854
|
-
return { launched: false, exitCode: null, command, error: launchError.error };
|
|
6855
|
-
}
|
|
6856
|
-
const exitCode = launchError.code;
|
|
6857
|
-
const elapsedMs = Date.now() - launchedAt;
|
|
6858
|
-
if (typeof exitCode === "number" && exitCode !== 0 && elapsedMs < 5000) {
|
|
6859
|
-
const error = `Pi exited during startup with code ${exitCode}.`;
|
|
6860
|
-
process.stderr.write(`${error} Falling back to Rig attach view.
|
|
6861
|
-
`);
|
|
6862
|
-
return { launched: false, exitCode, command, error };
|
|
6863
|
-
}
|
|
6864
|
-
return { launched: true, exitCode, command };
|
|
6865
|
-
}
|
|
6866
|
-
|
|
6867
7309
|
// packages/cli/src/commands/run.ts
|
|
6868
7310
|
function normalizeRemoteRunDetails(payload) {
|
|
6869
7311
|
const run = payload.run;
|
|
@@ -7088,20 +7530,13 @@ async function executeRun(context, args) {
|
|
|
7088
7530
|
throw new CliError2("run attach requires a run id.", 2);
|
|
7089
7531
|
}
|
|
7090
7532
|
let steered = false;
|
|
7091
|
-
|
|
7092
|
-
if (shouldTryPiAttach && messageOption.value?.trim()) {
|
|
7533
|
+
if (messageOption.value?.trim()) {
|
|
7093
7534
|
await steerRunViaServer(context, runId, messageOption.value.trim());
|
|
7094
7535
|
steered = true;
|
|
7095
7536
|
}
|
|
7096
|
-
if (shouldTryPiAttach) {
|
|
7097
|
-
const piSession = await launchPiRigSession(context, { runId });
|
|
7098
|
-
if (piSession.launched) {
|
|
7099
|
-
return { ok: true, group: "run", command, details: { runId, steered, mode: "pi", ...piSession } };
|
|
7100
|
-
}
|
|
7101
|
-
}
|
|
7102
7537
|
const attached = await attachRunOperatorView(context, {
|
|
7103
7538
|
runId,
|
|
7104
|
-
message:
|
|
7539
|
+
message: null,
|
|
7105
7540
|
once: once.value,
|
|
7106
7541
|
follow: follow.value,
|
|
7107
7542
|
pollMs: parsePositiveInt(pollMs.value, "--poll-ms", 2000)
|
|
@@ -7828,20 +8263,7 @@ async function executeTask(context, args, options) {
|
|
|
7828
8263
|
let attachDetails = null;
|
|
7829
8264
|
if (!detachResult.value && context.outputMode === "text") {
|
|
7830
8265
|
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7831
|
-
|
|
7832
|
-
const piSession = await launchPiRigSession(context, {
|
|
7833
|
-
runId: submitted.runId,
|
|
7834
|
-
taskId: selectedTaskId,
|
|
7835
|
-
title: titleResult.value ?? readTaskString(selectedTask ?? {}, "title"),
|
|
7836
|
-
runtimeAdapter
|
|
7837
|
-
});
|
|
7838
|
-
attachDetails = { mode: "pi", ...piSession };
|
|
7839
|
-
if (!piSession.launched) {
|
|
7840
|
-
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7841
|
-
}
|
|
7842
|
-
} else {
|
|
7843
|
-
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7844
|
-
}
|
|
8266
|
+
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7845
8267
|
} else if (context.outputMode === "text") {
|
|
7846
8268
|
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7847
8269
|
}
|
|
@@ -7928,7 +8350,7 @@ async function executeTask(context, args, options) {
|
|
|
7928
8350
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7929
8351
|
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7930
8352
|
import { resolve as resolve21 } from "path";
|
|
7931
|
-
import { spawn as
|
|
8353
|
+
import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
|
|
7932
8354
|
import { createInterface as createLineInterface } from "readline";
|
|
7933
8355
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
7934
8356
|
import {
|
|
@@ -7985,6 +8407,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7985
8407
|
RIG_SERVER_RUN_ID: input.runId,
|
|
7986
8408
|
RIG_TASK_ID: input.taskId,
|
|
7987
8409
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
8410
|
+
RIG_STEERING_POLL_MS: "0",
|
|
7988
8411
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7989
8412
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7990
8413
|
...githubBridgeEnv(githubToken)
|
|
@@ -8156,7 +8579,7 @@ async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
|
8156
8579
|
}
|
|
8157
8580
|
function createCommandRunner(binary) {
|
|
8158
8581
|
return async (args, options) => {
|
|
8159
|
-
const child =
|
|
8582
|
+
const child = spawn2(binary, [...args], {
|
|
8160
8583
|
cwd: options?.cwd,
|
|
8161
8584
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8162
8585
|
});
|
|
@@ -8580,6 +9003,69 @@ function appendAssistantTimelineFromRecord(input) {
|
|
|
8580
9003
|
}
|
|
8581
9004
|
return nextAssistantText;
|
|
8582
9005
|
}
|
|
9006
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
9007
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
9008
|
+
if (type === "response") {
|
|
9009
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
9010
|
+
const success = input.record.success !== false;
|
|
9011
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
9012
|
+
return true;
|
|
9013
|
+
}
|
|
9014
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
9015
|
+
id: input.nextRunLogId(),
|
|
9016
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
9017
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
9018
|
+
tone: success ? "tool" : "error",
|
|
9019
|
+
status: input.status,
|
|
9020
|
+
payload: input.record,
|
|
9021
|
+
createdAt: new Date().toISOString()
|
|
9022
|
+
});
|
|
9023
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
9024
|
+
return true;
|
|
9025
|
+
}
|
|
9026
|
+
if (type !== "extension_ui_request")
|
|
9027
|
+
return false;
|
|
9028
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
9029
|
+
let title = "Pi UI event";
|
|
9030
|
+
let detail = method;
|
|
9031
|
+
let tone = "info";
|
|
9032
|
+
if (method === "notify") {
|
|
9033
|
+
title = "Pi notification";
|
|
9034
|
+
detail = String(input.record.message ?? "");
|
|
9035
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
9036
|
+
} else if (method === "setStatus") {
|
|
9037
|
+
title = "Pi UI status";
|
|
9038
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
9039
|
+
tone = "tool";
|
|
9040
|
+
} else if (method === "setWidget") {
|
|
9041
|
+
title = "Pi UI widget";
|
|
9042
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
9043
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
9044
|
+
tone = "tool";
|
|
9045
|
+
} else if (method === "setTitle") {
|
|
9046
|
+
title = "Pi UI title";
|
|
9047
|
+
detail = String(input.record.title ?? "");
|
|
9048
|
+
tone = "tool";
|
|
9049
|
+
} else if (method === "set_editor_text") {
|
|
9050
|
+
title = "Pi editor update";
|
|
9051
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
9052
|
+
tone = "tool";
|
|
9053
|
+
} else {
|
|
9054
|
+
title = "Pi UI request";
|
|
9055
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
9056
|
+
}
|
|
9057
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
9058
|
+
id: input.nextRunLogId(),
|
|
9059
|
+
title,
|
|
9060
|
+
detail,
|
|
9061
|
+
tone,
|
|
9062
|
+
status: input.status,
|
|
9063
|
+
payload: input.record,
|
|
9064
|
+
createdAt: new Date().toISOString()
|
|
9065
|
+
});
|
|
9066
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9067
|
+
return true;
|
|
9068
|
+
}
|
|
8583
9069
|
function appendPiToolTimelineFromRecord(input) {
|
|
8584
9070
|
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
8585
9071
|
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
@@ -8598,7 +9084,7 @@ function appendPiToolTimelineFromRecord(input) {
|
|
|
8598
9084
|
}
|
|
8599
9085
|
function isNonRenderablePiProtocolRecord(record) {
|
|
8600
9086
|
const type = typeof record.type === "string" ? record.type : "";
|
|
8601
|
-
return type === "message_start" || type === "message_end" || type === "turn_start" || type === "turn_end" || type === "tool_result" || type === "message_update" && (!record.assistantMessageEvent || typeof record.assistantMessageEvent !== "object" || Array.isArray(record.assistantMessageEvent) || record.assistantMessageEvent.type !== "text_delta");
|
|
9087
|
+
return type === "agent_start" || type === "agent_end" || type === "message_start" || type === "message_end" || type === "turn_start" || type === "turn_end" || type === "tool_result" || type === "message_update" && (!record.assistantMessageEvent || typeof record.assistantMessageEvent !== "object" || Array.isArray(record.assistantMessageEvent) || record.assistantMessageEvent.type !== "text_delta");
|
|
8602
9088
|
}
|
|
8603
9089
|
function appendToolTimelineFromLog(input) {
|
|
8604
9090
|
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
@@ -8761,11 +9247,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8761
9247
|
...input.model ? ["--model", input.model] : [],
|
|
8762
9248
|
"--prompt"
|
|
8763
9249
|
] : input.runtimeAdapter === "pi" ? [
|
|
8764
|
-
"
|
|
8765
|
-
"--verbose",
|
|
8766
|
-
"--mode",
|
|
8767
|
-
"json",
|
|
8768
|
-
"--no-session",
|
|
9250
|
+
"__rig_pi_session_daemon__",
|
|
8769
9251
|
...input.model ? ["--model", input.model] : []
|
|
8770
9252
|
] : [
|
|
8771
9253
|
"--print",
|
|
@@ -8862,7 +9344,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8862
9344
|
projectRoot: context.projectRoot,
|
|
8863
9345
|
runId: input.runId,
|
|
8864
9346
|
stage,
|
|
8865
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
9347
|
+
detail: stage === "Launch Pi" ? "Worker Pi SDK session daemon starting; local frontend will attach over Rig-proxied WebSocket." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running in the worker runtime." : null,
|
|
8866
9348
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
8867
9349
|
});
|
|
8868
9350
|
}
|
|
@@ -8903,6 +9385,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8903
9385
|
let latestProviderCommand = null;
|
|
8904
9386
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
8905
9387
|
let snapshotSidecarPromise = null;
|
|
9388
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
8906
9389
|
let dirtyBaselineApplied = false;
|
|
8907
9390
|
const childEnv = {
|
|
8908
9391
|
...process.env,
|
|
@@ -8955,6 +9438,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8955
9438
|
detail: detail ?? "Verifier review is running."
|
|
8956
9439
|
});
|
|
8957
9440
|
};
|
|
9441
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
8958
9442
|
const handleWrapperEvent = (rawPayload) => {
|
|
8959
9443
|
let event = null;
|
|
8960
9444
|
try {
|
|
@@ -8970,6 +9454,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8970
9454
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
8971
9455
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
8972
9456
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
9457
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
8973
9458
|
latestRuntimeBranch = runtimeId;
|
|
8974
9459
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
8975
9460
|
runtimeId: runtimeId ?? null,
|
|
@@ -9007,7 +9492,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9007
9492
|
});
|
|
9008
9493
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
9009
9494
|
}
|
|
9010
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
9495
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
9011
9496
|
snapshotSidecarPromise = (async () => {
|
|
9012
9497
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
9013
9498
|
projectRoot: context.projectRoot,
|
|
@@ -9089,9 +9574,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9089
9574
|
}
|
|
9090
9575
|
return true;
|
|
9091
9576
|
}
|
|
9577
|
+
if (event.type === "pi.sessiond.starting" || event.type === "pi.sessiond.ready" || event.type === "pi.session.event_stream.connected" || event.type === "pi.prompt.sent" || event.type === "pi.prompt.waiting" || event.type === "pi.session.agent_end" || event.type === "pi.session.error") {
|
|
9578
|
+
const title = event.type === "pi.sessiond.starting" ? "Starting worker Pi session daemon" : event.type === "pi.sessiond.ready" ? "Worker Pi daemon ready" : event.type === "pi.session.event_stream.connected" ? "Worker Pi event stream connected" : event.type === "pi.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.prompt.waiting" ? "Worker Pi prompt waiting" : event.type === "pi.session.agent_end" ? "Worker Pi turn complete" : "Worker Pi session error";
|
|
9579
|
+
const detail = event.type === "pi.sessiond.ready" ? "Daemon accepted control connection; waiting for SDK session metadata." : event.type === "pi.sessiond.starting" ? String(payload.workspaceDir ?? "worker runtime") : event.type === "pi.prompt.sent" ? `${String(payload.bytes ?? "unknown")} prompt bytes sent.` : event.type === "pi.prompt.waiting" ? String(payload.reason ?? "empty prompt") : event.type === "pi.session.error" ? String(payload.message ?? "session error") : String(payload.sessionId ?? payload.runId ?? "worker Pi session");
|
|
9580
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9581
|
+
id: nextRunLogId(),
|
|
9582
|
+
title,
|
|
9583
|
+
detail,
|
|
9584
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
9585
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
9586
|
+
payload: {
|
|
9587
|
+
eventType: event.type,
|
|
9588
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
9589
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
9590
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
9591
|
+
},
|
|
9592
|
+
createdAt: new Date().toISOString()
|
|
9593
|
+
});
|
|
9594
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9595
|
+
return true;
|
|
9596
|
+
}
|
|
9597
|
+
if (event.type === "pi.session.ready") {
|
|
9598
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
9599
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
9600
|
+
if (privateMetadata) {
|
|
9601
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
9602
|
+
piSession: publicMetadata,
|
|
9603
|
+
piSessionPrivate: privateMetadata
|
|
9604
|
+
});
|
|
9605
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
9606
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9607
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
9608
|
+
title: "Worker Pi session ready",
|
|
9609
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
9610
|
+
tone: "info",
|
|
9611
|
+
status: "running",
|
|
9612
|
+
payload: {
|
|
9613
|
+
sessionId: sessionId || null,
|
|
9614
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
9615
|
+
},
|
|
9616
|
+
createdAt: new Date().toISOString()
|
|
9617
|
+
});
|
|
9618
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
9619
|
+
}
|
|
9620
|
+
return true;
|
|
9621
|
+
}
|
|
9622
|
+
if (event.type === "pi.rpc.prompt.sent" || event.type === "pi.rpc.steering.delivered" || event.type === "pi.rpc.steering.poll.failed" || event.type === "pi.rpc.extension_ui.cancelled") {
|
|
9623
|
+
const title = event.type === "pi.rpc.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.rpc.steering.delivered" ? "Delivered steering to worker Pi" : event.type === "pi.rpc.steering.poll.failed" ? "Worker Pi steering poll failed" : "Pi RPC UI request auto-cancelled";
|
|
9624
|
+
const detail = event.type === "pi.rpc.prompt.sent" ? `${String(payload.kind ?? "prompt")} prompt (${String(payload.bytes ?? "unknown")} bytes)` : event.type === "pi.rpc.steering.delivered" ? `${String(payload.actor ?? "operator")}: ${String(payload.message ?? "")}`.slice(0, 500) : event.type === "pi.rpc.steering.poll.failed" ? String(payload.error ?? "steering poll failed") : `${String(payload.method ?? "ui")}: ${String(payload.reason ?? "noninteractive worker session")}`;
|
|
9625
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9626
|
+
id: nextRunLogId(),
|
|
9627
|
+
title,
|
|
9628
|
+
detail,
|
|
9629
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
9630
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
9631
|
+
payload,
|
|
9632
|
+
createdAt: new Date().toISOString()
|
|
9633
|
+
});
|
|
9634
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9635
|
+
return true;
|
|
9636
|
+
}
|
|
9092
9637
|
return false;
|
|
9093
9638
|
};
|
|
9094
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
9095
9639
|
const handleAgentStdoutLine = (line) => {
|
|
9096
9640
|
const trimmed = line.trim();
|
|
9097
9641
|
if (!trimmed)
|
|
@@ -9125,6 +9669,15 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9125
9669
|
try {
|
|
9126
9670
|
const record = JSON.parse(trimmed);
|
|
9127
9671
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
9672
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
9673
|
+
projectRoot: context.projectRoot,
|
|
9674
|
+
runId: input.runId,
|
|
9675
|
+
record,
|
|
9676
|
+
status: liveLogStatus,
|
|
9677
|
+
nextRunLogId
|
|
9678
|
+
})) {
|
|
9679
|
+
return;
|
|
9680
|
+
}
|
|
9128
9681
|
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
9129
9682
|
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
9130
9683
|
return;
|
|
@@ -9270,7 +9823,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9270
9823
|
}
|
|
9271
9824
|
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
9272
9825
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
9273
|
-
const child =
|
|
9826
|
+
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
9274
9827
|
cwd: context.projectRoot,
|
|
9275
9828
|
env: childEnv,
|
|
9276
9829
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -9597,7 +10150,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9597
10150
|
startedAt,
|
|
9598
10151
|
finishedAt: requestedAt
|
|
9599
10152
|
});
|
|
9600
|
-
|
|
10153
|
+
process.exit(0);
|
|
9601
10154
|
}
|
|
9602
10155
|
const runPiPrFeedbackFix = async (message2) => {
|
|
9603
10156
|
appendPiStageLog({
|
|
@@ -9617,7 +10170,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9617
10170
|
});
|
|
9618
10171
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Steering Pi from PR feedback" });
|
|
9619
10172
|
const feedbackCommand = buildHostAgentCommand(message2);
|
|
9620
|
-
const child =
|
|
10173
|
+
const child = spawn2(feedbackCommand[0], feedbackCommand.slice(1), {
|
|
9621
10174
|
cwd: latestRuntimeWorkspace ?? context.projectRoot,
|
|
9622
10175
|
env: childEnv,
|
|
9623
10176
|
stdio: ["pipe", "pipe", "pipe"]
|