@h-rig/runtime 0.0.6-alpha.21 → 0.0.6-alpha.22
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.
|
@@ -567,6 +567,7 @@ var init_backend_bwrap = __esm(() => {
|
|
|
567
567
|
});
|
|
568
568
|
|
|
569
569
|
// packages/runtime/src/control-plane/agent-wrapper.ts
|
|
570
|
+
import { createRequire } from "module";
|
|
570
571
|
import { resolve as resolve35 } from "path";
|
|
571
572
|
import { existsSync as existsSync33, mkdirSync as mkdirSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
572
573
|
|
|
@@ -8884,6 +8885,7 @@ function formatJsonRpcError(error) {
|
|
|
8884
8885
|
}
|
|
8885
8886
|
|
|
8886
8887
|
// packages/runtime/src/control-plane/agent-wrapper.ts
|
|
8888
|
+
var requireFromRuntime = createRequire(import.meta.url);
|
|
8887
8889
|
async function finalizeRuntimeSnapshot(snapshotSidecar, providerCommand, exitCode, context) {
|
|
8888
8890
|
try {
|
|
8889
8891
|
await snapshotSidecar.finalize(providerCommand, exitCode);
|
|
@@ -9029,14 +9031,26 @@ async function runAgentWrapper(options = {}) {
|
|
|
9029
9031
|
},
|
|
9030
9032
|
command: providerCommand
|
|
9031
9033
|
})).command;
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9034
|
+
if (provider === "pi" && isPiRpcArgs(providerArgs)) {
|
|
9035
|
+
const prompt = await readProcessStdin();
|
|
9036
|
+
exitCode = await runPiRpcProvider({
|
|
9037
|
+
command,
|
|
9038
|
+
cwd: runtime.workspaceDir,
|
|
9039
|
+
env,
|
|
9040
|
+
prompt,
|
|
9041
|
+
runId: process.env.RIG_SERVER_RUN_ID?.trim() || undefined,
|
|
9042
|
+
sessionName: process.env.RIG_SERVER_RUN_ID?.trim() ? `Rig ${process.env.RIG_SERVER_RUN_ID.trim()}` : `Rig ${runtime.taskId}`
|
|
9043
|
+
});
|
|
9044
|
+
} else {
|
|
9045
|
+
const proc = Bun.spawn(command, {
|
|
9046
|
+
cwd: runtime.workspaceDir,
|
|
9047
|
+
env,
|
|
9048
|
+
stdin: "inherit",
|
|
9049
|
+
stdout: "inherit",
|
|
9050
|
+
stderr: "inherit"
|
|
9051
|
+
});
|
|
9052
|
+
exitCode = await proc.exited;
|
|
9053
|
+
}
|
|
9040
9054
|
}
|
|
9041
9055
|
if (snapshotSidecar) {
|
|
9042
9056
|
await finalizeRuntimeSnapshot(snapshotSidecar, providerCommand, exitCode, {
|
|
@@ -9073,6 +9087,215 @@ async function runAgentWrapper(options = {}) {
|
|
|
9073
9087
|
}
|
|
9074
9088
|
return finalExitCode;
|
|
9075
9089
|
}
|
|
9090
|
+
function parseJsonRecord(line) {
|
|
9091
|
+
try {
|
|
9092
|
+
const parsed = JSON.parse(line);
|
|
9093
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
9094
|
+
} catch {
|
|
9095
|
+
return null;
|
|
9096
|
+
}
|
|
9097
|
+
}
|
|
9098
|
+
async function readProcessStdin() {
|
|
9099
|
+
if (process.stdin.isTTY)
|
|
9100
|
+
return "";
|
|
9101
|
+
return await new Promise((resolveRead) => {
|
|
9102
|
+
let data = "";
|
|
9103
|
+
process.stdin.setEncoding("utf8");
|
|
9104
|
+
process.stdin.on("data", (chunk) => {
|
|
9105
|
+
data += String(chunk);
|
|
9106
|
+
});
|
|
9107
|
+
process.stdin.on("end", () => resolveRead(data));
|
|
9108
|
+
process.stdin.resume();
|
|
9109
|
+
});
|
|
9110
|
+
}
|
|
9111
|
+
async function pumpReadableLines(stream, onLine) {
|
|
9112
|
+
if (!stream)
|
|
9113
|
+
return;
|
|
9114
|
+
const reader = stream.getReader();
|
|
9115
|
+
const decoder = new TextDecoder;
|
|
9116
|
+
let buffer = "";
|
|
9117
|
+
try {
|
|
9118
|
+
while (true) {
|
|
9119
|
+
const { done, value } = await reader.read();
|
|
9120
|
+
if (done)
|
|
9121
|
+
break;
|
|
9122
|
+
buffer += decoder.decode(value, { stream: true });
|
|
9123
|
+
const parts = buffer.split(/\r?\n/);
|
|
9124
|
+
buffer = parts.pop() ?? "";
|
|
9125
|
+
for (const part of parts)
|
|
9126
|
+
onLine(part);
|
|
9127
|
+
}
|
|
9128
|
+
buffer += decoder.decode();
|
|
9129
|
+
if (buffer)
|
|
9130
|
+
onLine(buffer);
|
|
9131
|
+
} finally {
|
|
9132
|
+
reader.releaseLock();
|
|
9133
|
+
}
|
|
9134
|
+
}
|
|
9135
|
+
function isBlockingPiRpcUiRequest(record) {
|
|
9136
|
+
if (record.type !== "extension_ui_request")
|
|
9137
|
+
return false;
|
|
9138
|
+
return record.method === "select" || record.method === "confirm" || record.method === "input" || record.method === "editor";
|
|
9139
|
+
}
|
|
9140
|
+
function writeRpcCommand(stdin, command) {
|
|
9141
|
+
stdin.write(`${JSON.stringify(command)}
|
|
9142
|
+
`);
|
|
9143
|
+
}
|
|
9144
|
+
function joinUrl(baseUrl, pathname) {
|
|
9145
|
+
return `${baseUrl.replace(/\/+$/, "")}${pathname.startsWith("/") ? pathname : `/${pathname}`}`;
|
|
9146
|
+
}
|
|
9147
|
+
async function readQueuedSteeringFromServer(input) {
|
|
9148
|
+
if (!input.serverUrl || !input.runId)
|
|
9149
|
+
return [];
|
|
9150
|
+
const headers = {};
|
|
9151
|
+
if (input.authToken)
|
|
9152
|
+
headers.authorization = `Bearer ${input.authToken}`;
|
|
9153
|
+
const response = await fetch(joinUrl(input.serverUrl, `/api/runs/${encodeURIComponent(input.runId)}/steering?ack=1`), { headers });
|
|
9154
|
+
if (!response.ok)
|
|
9155
|
+
return [];
|
|
9156
|
+
const payload = await response.json().catch(() => null);
|
|
9157
|
+
const messages = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.messages : null;
|
|
9158
|
+
return Array.isArray(messages) ? messages.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
9159
|
+
}
|
|
9160
|
+
function steeringMessageText(entry) {
|
|
9161
|
+
const message = typeof entry.message === "string" ? entry.message.trim() : "";
|
|
9162
|
+
return message || null;
|
|
9163
|
+
}
|
|
9164
|
+
function isPiRpcArgs(args) {
|
|
9165
|
+
return cliOptionValue(args, "--mode") === "rpc";
|
|
9166
|
+
}
|
|
9167
|
+
async function runPiRpcProvider(input) {
|
|
9168
|
+
const stdout = input.stdout ?? process.stdout;
|
|
9169
|
+
const stderr = input.stderr ?? process.stderr;
|
|
9170
|
+
const proc = Bun.spawn(input.command, {
|
|
9171
|
+
cwd: input.cwd,
|
|
9172
|
+
env: input.env,
|
|
9173
|
+
stdin: "pipe",
|
|
9174
|
+
stdout: "pipe",
|
|
9175
|
+
stderr: "pipe"
|
|
9176
|
+
});
|
|
9177
|
+
let sawAgentEnd = false;
|
|
9178
|
+
let promptError = null;
|
|
9179
|
+
let stdinOpen = true;
|
|
9180
|
+
let steeringPollStopped = false;
|
|
9181
|
+
const closeStdin = () => {
|
|
9182
|
+
if (!stdinOpen)
|
|
9183
|
+
return;
|
|
9184
|
+
stdinOpen = false;
|
|
9185
|
+
try {
|
|
9186
|
+
steeringPollStopped = true;
|
|
9187
|
+
proc.stdin.end();
|
|
9188
|
+
} catch {}
|
|
9189
|
+
};
|
|
9190
|
+
const send = (command) => {
|
|
9191
|
+
if (!stdinOpen)
|
|
9192
|
+
return;
|
|
9193
|
+
try {
|
|
9194
|
+
writeRpcCommand(proc.stdin, command);
|
|
9195
|
+
} catch (error) {
|
|
9196
|
+
promptError ??= error instanceof Error ? error.message : String(error);
|
|
9197
|
+
}
|
|
9198
|
+
};
|
|
9199
|
+
const forwardSigterm = () => {
|
|
9200
|
+
try {
|
|
9201
|
+
proc.kill("SIGTERM");
|
|
9202
|
+
} catch {}
|
|
9203
|
+
};
|
|
9204
|
+
process.once("SIGTERM", forwardSigterm);
|
|
9205
|
+
const pollSteering = async () => {
|
|
9206
|
+
const serverUrl = input.env.RIG_SERVER_URL || input.env.RIG_SERVER_BASE_URL;
|
|
9207
|
+
const authToken = input.env.RIG_AUTH_TOKEN || input.env.RIG_SERVER_AUTH_TOKEN;
|
|
9208
|
+
while (!steeringPollStopped && stdinOpen) {
|
|
9209
|
+
try {
|
|
9210
|
+
const messages = await readQueuedSteeringFromServer({ serverUrl, authToken, runId: input.runId });
|
|
9211
|
+
for (const message of messages) {
|
|
9212
|
+
const text = steeringMessageText(message);
|
|
9213
|
+
if (!text || !stdinOpen)
|
|
9214
|
+
continue;
|
|
9215
|
+
send({
|
|
9216
|
+
id: typeof message.id === "string" ? `rig_steer_${message.id}` : `rig_steer_${Date.now()}`,
|
|
9217
|
+
type: "prompt",
|
|
9218
|
+
message: text,
|
|
9219
|
+
streamingBehavior: "steer"
|
|
9220
|
+
});
|
|
9221
|
+
emitWrapperEvent("pi.rpc.steering.delivered", {
|
|
9222
|
+
runId: input.runId ?? null,
|
|
9223
|
+
steeringId: typeof message.id === "string" ? message.id : null,
|
|
9224
|
+
actor: typeof message.actor === "string" ? message.actor : "operator",
|
|
9225
|
+
message: text
|
|
9226
|
+
});
|
|
9227
|
+
}
|
|
9228
|
+
} catch (error) {
|
|
9229
|
+
emitWrapperEvent("pi.rpc.steering.poll.failed", {
|
|
9230
|
+
runId: input.runId ?? null,
|
|
9231
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9232
|
+
});
|
|
9233
|
+
}
|
|
9234
|
+
await sleep(1000);
|
|
9235
|
+
}
|
|
9236
|
+
};
|
|
9237
|
+
const stdoutPump = pumpReadableLines(proc.stdout, (line) => {
|
|
9238
|
+
stdout.write(`${line}
|
|
9239
|
+
`);
|
|
9240
|
+
const record = parseJsonRecord(line.trim());
|
|
9241
|
+
if (!record)
|
|
9242
|
+
return;
|
|
9243
|
+
if (record.type === "agent_end") {
|
|
9244
|
+
sawAgentEnd = true;
|
|
9245
|
+
closeStdin();
|
|
9246
|
+
return;
|
|
9247
|
+
}
|
|
9248
|
+
if (record.type === "response" && record.command === "prompt" && record.success === false) {
|
|
9249
|
+
promptError = typeof record.error === "string" ? record.error : "Pi RPC prompt failed.";
|
|
9250
|
+
closeStdin();
|
|
9251
|
+
return;
|
|
9252
|
+
}
|
|
9253
|
+
if (isBlockingPiRpcUiRequest(record)) {
|
|
9254
|
+
const id = typeof record.id === "string" ? record.id : "";
|
|
9255
|
+
if (id) {
|
|
9256
|
+
send({ type: "extension_ui_response", id, cancelled: true });
|
|
9257
|
+
emitWrapperEvent("pi.rpc.extension_ui.cancelled", {
|
|
9258
|
+
id,
|
|
9259
|
+
method: record.method,
|
|
9260
|
+
reason: "noninteractive Rig worker RPC session"
|
|
9261
|
+
});
|
|
9262
|
+
}
|
|
9263
|
+
}
|
|
9264
|
+
});
|
|
9265
|
+
const stderrPump = pumpReadableLines(proc.stderr, (line) => {
|
|
9266
|
+
stderr.write(`${line}
|
|
9267
|
+
`);
|
|
9268
|
+
});
|
|
9269
|
+
if (input.sessionName?.trim()) {
|
|
9270
|
+
send({ id: "rig_set_session_name", type: "set_session_name", name: input.sessionName.trim() });
|
|
9271
|
+
}
|
|
9272
|
+
const steeringPollPromise = pollSteering();
|
|
9273
|
+
if (input.prompt.trim()) {
|
|
9274
|
+
send({ id: "rig_initial_prompt", type: "prompt", message: input.prompt });
|
|
9275
|
+
emitWrapperEvent("pi.rpc.prompt.sent", {
|
|
9276
|
+
runId: input.runId ?? null,
|
|
9277
|
+
kind: "initial",
|
|
9278
|
+
bytes: Buffer.byteLength(input.prompt)
|
|
9279
|
+
});
|
|
9280
|
+
} else {
|
|
9281
|
+
closeStdin();
|
|
9282
|
+
}
|
|
9283
|
+
const exitCode = await proc.exited;
|
|
9284
|
+
process.off("SIGTERM", forwardSigterm);
|
|
9285
|
+
steeringPollStopped = true;
|
|
9286
|
+
await Promise.all([stdoutPump, stderrPump, steeringPollPromise]);
|
|
9287
|
+
if (promptError) {
|
|
9288
|
+
stderr.write(`[rig-agent] Pi RPC prompt failed: ${promptError}
|
|
9289
|
+
`);
|
|
9290
|
+
return exitCode === 0 ? 1 : exitCode;
|
|
9291
|
+
}
|
|
9292
|
+
if (input.prompt.trim() && !sawAgentEnd && exitCode === 0) {
|
|
9293
|
+
stderr.write(`[rig-agent] Pi RPC exited before emitting agent_end.
|
|
9294
|
+
`);
|
|
9295
|
+
return 1;
|
|
9296
|
+
}
|
|
9297
|
+
return exitCode;
|
|
9298
|
+
}
|
|
9076
9299
|
function resolveFinalProviderExitCode(input) {
|
|
9077
9300
|
if (input.providerExitCode !== 0) {
|
|
9078
9301
|
return input.providerExitCode;
|
|
@@ -9116,6 +9339,12 @@ function buildProviderArgs(provider, runtime, argv) {
|
|
|
9116
9339
|
piArgs.unshift(normalizePiModelForProvider(model, piProvider));
|
|
9117
9340
|
piArgs.unshift("--model");
|
|
9118
9341
|
}
|
|
9342
|
+
if (!hasCliOption(piArgs, "--mode")) {
|
|
9343
|
+
piArgs.push("--mode", process.env.RIG_PI_TRANSPORT?.trim() === "print" ? "json" : "rpc");
|
|
9344
|
+
if (process.env.RIG_PI_TRANSPORT?.trim() === "print" && !hasCliOption(piArgs, "--print")) {
|
|
9345
|
+
piArgs.push("--print");
|
|
9346
|
+
}
|
|
9347
|
+
}
|
|
9119
9348
|
return piArgs;
|
|
9120
9349
|
}
|
|
9121
9350
|
return [
|
|
@@ -9213,17 +9442,38 @@ function normalizePiModelForProvider(model, provider) {
|
|
|
9213
9442
|
}
|
|
9214
9443
|
return model;
|
|
9215
9444
|
}
|
|
9445
|
+
function resolveFromShellPath(binary) {
|
|
9446
|
+
const resolved = Bun.spawnSync(["sh", "-lc", `command -v ${binary}`], {
|
|
9447
|
+
stdout: "pipe",
|
|
9448
|
+
stderr: "ignore",
|
|
9449
|
+
stdin: "ignore",
|
|
9450
|
+
env: process.env
|
|
9451
|
+
});
|
|
9452
|
+
if (resolved.exitCode !== 0)
|
|
9453
|
+
return null;
|
|
9454
|
+
const path = resolved.stdout.toString().trim().split(/\r?\n/)[0]?.trim();
|
|
9455
|
+
return path || null;
|
|
9456
|
+
}
|
|
9457
|
+
function resolveBundledPiBinary() {
|
|
9458
|
+
try {
|
|
9459
|
+
const packageJson = requireFromRuntime.resolve("@earendil-works/pi-coding-agent/package.json");
|
|
9460
|
+
const binaryPath = resolve35(packageJson, "..", "dist", "cli.js");
|
|
9461
|
+
return existsSync33(binaryPath) ? binaryPath : null;
|
|
9462
|
+
} catch {
|
|
9463
|
+
return null;
|
|
9464
|
+
}
|
|
9465
|
+
}
|
|
9216
9466
|
function providerBinary(provider) {
|
|
9217
9467
|
if (provider === "codex") {
|
|
9218
|
-
return Bun.which("codex") || "codex";
|
|
9468
|
+
return resolveFromShellPath("codex") || Bun.which("codex") || "codex";
|
|
9219
9469
|
}
|
|
9220
9470
|
if (provider === "pi") {
|
|
9221
|
-
return Bun.which("pi") || "pi";
|
|
9471
|
+
return process.env.RIG_PI_BINARY?.trim() || resolveBundledPiBinary() || resolveFromShellPath("pi") || Bun.which("pi") || "pi";
|
|
9222
9472
|
}
|
|
9223
9473
|
try {
|
|
9224
9474
|
return resolveClaudeBinaryPath();
|
|
9225
9475
|
} catch {
|
|
9226
|
-
return Bun.which("claude") || "claude";
|
|
9476
|
+
return resolveFromShellPath("claude") || Bun.which("claude") || "claude";
|
|
9227
9477
|
}
|
|
9228
9478
|
}
|
|
9229
9479
|
function emitWrapperEvent(type, payload) {
|
|
@@ -567,6 +567,7 @@ var init_backend_bwrap = __esm(() => {
|
|
|
567
567
|
});
|
|
568
568
|
|
|
569
569
|
// packages/runtime/src/control-plane/agent-wrapper.ts
|
|
570
|
+
import { createRequire } from "module";
|
|
570
571
|
import { resolve as resolve35 } from "path";
|
|
571
572
|
import { existsSync as existsSync33, mkdirSync as mkdirSync19, writeFileSync as writeFileSync13 } from "fs";
|
|
572
573
|
|
|
@@ -9152,6 +9153,7 @@ function formatJsonRpcError(error) {
|
|
|
9152
9153
|
}
|
|
9153
9154
|
|
|
9154
9155
|
// packages/runtime/src/control-plane/agent-wrapper.ts
|
|
9156
|
+
var requireFromRuntime = createRequire(import.meta.url);
|
|
9155
9157
|
async function finalizeRuntimeSnapshot(snapshotSidecar, providerCommand, exitCode, context) {
|
|
9156
9158
|
try {
|
|
9157
9159
|
await snapshotSidecar.finalize(providerCommand, exitCode);
|
|
@@ -9297,14 +9299,26 @@ async function runAgentWrapper(options = {}) {
|
|
|
9297
9299
|
},
|
|
9298
9300
|
command: providerCommand
|
|
9299
9301
|
})).command;
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9302
|
+
if (provider === "pi" && isPiRpcArgs(providerArgs)) {
|
|
9303
|
+
const prompt = await readProcessStdin();
|
|
9304
|
+
exitCode = await runPiRpcProvider({
|
|
9305
|
+
command,
|
|
9306
|
+
cwd: runtime.workspaceDir,
|
|
9307
|
+
env,
|
|
9308
|
+
prompt,
|
|
9309
|
+
runId: process.env.RIG_SERVER_RUN_ID?.trim() || undefined,
|
|
9310
|
+
sessionName: process.env.RIG_SERVER_RUN_ID?.trim() ? `Rig ${process.env.RIG_SERVER_RUN_ID.trim()}` : `Rig ${runtime.taskId}`
|
|
9311
|
+
});
|
|
9312
|
+
} else {
|
|
9313
|
+
const proc = Bun.spawn(command, {
|
|
9314
|
+
cwd: runtime.workspaceDir,
|
|
9315
|
+
env,
|
|
9316
|
+
stdin: "inherit",
|
|
9317
|
+
stdout: "inherit",
|
|
9318
|
+
stderr: "inherit"
|
|
9319
|
+
});
|
|
9320
|
+
exitCode = await proc.exited;
|
|
9321
|
+
}
|
|
9308
9322
|
}
|
|
9309
9323
|
if (snapshotSidecar) {
|
|
9310
9324
|
await finalizeRuntimeSnapshot(snapshotSidecar, providerCommand, exitCode, {
|
|
@@ -9341,6 +9355,215 @@ async function runAgentWrapper(options = {}) {
|
|
|
9341
9355
|
}
|
|
9342
9356
|
return finalExitCode;
|
|
9343
9357
|
}
|
|
9358
|
+
function parseJsonRecord(line) {
|
|
9359
|
+
try {
|
|
9360
|
+
const parsed = JSON.parse(line);
|
|
9361
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
9362
|
+
} catch {
|
|
9363
|
+
return null;
|
|
9364
|
+
}
|
|
9365
|
+
}
|
|
9366
|
+
async function readProcessStdin() {
|
|
9367
|
+
if (process.stdin.isTTY)
|
|
9368
|
+
return "";
|
|
9369
|
+
return await new Promise((resolveRead) => {
|
|
9370
|
+
let data = "";
|
|
9371
|
+
process.stdin.setEncoding("utf8");
|
|
9372
|
+
process.stdin.on("data", (chunk) => {
|
|
9373
|
+
data += String(chunk);
|
|
9374
|
+
});
|
|
9375
|
+
process.stdin.on("end", () => resolveRead(data));
|
|
9376
|
+
process.stdin.resume();
|
|
9377
|
+
});
|
|
9378
|
+
}
|
|
9379
|
+
async function pumpReadableLines(stream, onLine) {
|
|
9380
|
+
if (!stream)
|
|
9381
|
+
return;
|
|
9382
|
+
const reader = stream.getReader();
|
|
9383
|
+
const decoder = new TextDecoder;
|
|
9384
|
+
let buffer = "";
|
|
9385
|
+
try {
|
|
9386
|
+
while (true) {
|
|
9387
|
+
const { done, value } = await reader.read();
|
|
9388
|
+
if (done)
|
|
9389
|
+
break;
|
|
9390
|
+
buffer += decoder.decode(value, { stream: true });
|
|
9391
|
+
const parts = buffer.split(/\r?\n/);
|
|
9392
|
+
buffer = parts.pop() ?? "";
|
|
9393
|
+
for (const part of parts)
|
|
9394
|
+
onLine(part);
|
|
9395
|
+
}
|
|
9396
|
+
buffer += decoder.decode();
|
|
9397
|
+
if (buffer)
|
|
9398
|
+
onLine(buffer);
|
|
9399
|
+
} finally {
|
|
9400
|
+
reader.releaseLock();
|
|
9401
|
+
}
|
|
9402
|
+
}
|
|
9403
|
+
function isBlockingPiRpcUiRequest(record) {
|
|
9404
|
+
if (record.type !== "extension_ui_request")
|
|
9405
|
+
return false;
|
|
9406
|
+
return record.method === "select" || record.method === "confirm" || record.method === "input" || record.method === "editor";
|
|
9407
|
+
}
|
|
9408
|
+
function writeRpcCommand(stdin, command) {
|
|
9409
|
+
stdin.write(`${JSON.stringify(command)}
|
|
9410
|
+
`);
|
|
9411
|
+
}
|
|
9412
|
+
function joinUrl(baseUrl, pathname) {
|
|
9413
|
+
return `${baseUrl.replace(/\/+$/, "")}${pathname.startsWith("/") ? pathname : `/${pathname}`}`;
|
|
9414
|
+
}
|
|
9415
|
+
async function readQueuedSteeringFromServer(input) {
|
|
9416
|
+
if (!input.serverUrl || !input.runId)
|
|
9417
|
+
return [];
|
|
9418
|
+
const headers = {};
|
|
9419
|
+
if (input.authToken)
|
|
9420
|
+
headers.authorization = `Bearer ${input.authToken}`;
|
|
9421
|
+
const response = await fetch(joinUrl(input.serverUrl, `/api/runs/${encodeURIComponent(input.runId)}/steering?ack=1`), { headers });
|
|
9422
|
+
if (!response.ok)
|
|
9423
|
+
return [];
|
|
9424
|
+
const payload = await response.json().catch(() => null);
|
|
9425
|
+
const messages = payload && typeof payload === "object" && !Array.isArray(payload) ? payload.messages : null;
|
|
9426
|
+
return Array.isArray(messages) ? messages.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
9427
|
+
}
|
|
9428
|
+
function steeringMessageText(entry) {
|
|
9429
|
+
const message = typeof entry.message === "string" ? entry.message.trim() : "";
|
|
9430
|
+
return message || null;
|
|
9431
|
+
}
|
|
9432
|
+
function isPiRpcArgs(args) {
|
|
9433
|
+
return cliOptionValue(args, "--mode") === "rpc";
|
|
9434
|
+
}
|
|
9435
|
+
async function runPiRpcProvider(input) {
|
|
9436
|
+
const stdout = input.stdout ?? process.stdout;
|
|
9437
|
+
const stderr = input.stderr ?? process.stderr;
|
|
9438
|
+
const proc = Bun.spawn(input.command, {
|
|
9439
|
+
cwd: input.cwd,
|
|
9440
|
+
env: input.env,
|
|
9441
|
+
stdin: "pipe",
|
|
9442
|
+
stdout: "pipe",
|
|
9443
|
+
stderr: "pipe"
|
|
9444
|
+
});
|
|
9445
|
+
let sawAgentEnd = false;
|
|
9446
|
+
let promptError = null;
|
|
9447
|
+
let stdinOpen = true;
|
|
9448
|
+
let steeringPollStopped = false;
|
|
9449
|
+
const closeStdin = () => {
|
|
9450
|
+
if (!stdinOpen)
|
|
9451
|
+
return;
|
|
9452
|
+
stdinOpen = false;
|
|
9453
|
+
try {
|
|
9454
|
+
steeringPollStopped = true;
|
|
9455
|
+
proc.stdin.end();
|
|
9456
|
+
} catch {}
|
|
9457
|
+
};
|
|
9458
|
+
const send = (command) => {
|
|
9459
|
+
if (!stdinOpen)
|
|
9460
|
+
return;
|
|
9461
|
+
try {
|
|
9462
|
+
writeRpcCommand(proc.stdin, command);
|
|
9463
|
+
} catch (error) {
|
|
9464
|
+
promptError ??= error instanceof Error ? error.message : String(error);
|
|
9465
|
+
}
|
|
9466
|
+
};
|
|
9467
|
+
const forwardSigterm = () => {
|
|
9468
|
+
try {
|
|
9469
|
+
proc.kill("SIGTERM");
|
|
9470
|
+
} catch {}
|
|
9471
|
+
};
|
|
9472
|
+
process.once("SIGTERM", forwardSigterm);
|
|
9473
|
+
const pollSteering = async () => {
|
|
9474
|
+
const serverUrl = input.env.RIG_SERVER_URL || input.env.RIG_SERVER_BASE_URL;
|
|
9475
|
+
const authToken = input.env.RIG_AUTH_TOKEN || input.env.RIG_SERVER_AUTH_TOKEN;
|
|
9476
|
+
while (!steeringPollStopped && stdinOpen) {
|
|
9477
|
+
try {
|
|
9478
|
+
const messages = await readQueuedSteeringFromServer({ serverUrl, authToken, runId: input.runId });
|
|
9479
|
+
for (const message of messages) {
|
|
9480
|
+
const text = steeringMessageText(message);
|
|
9481
|
+
if (!text || !stdinOpen)
|
|
9482
|
+
continue;
|
|
9483
|
+
send({
|
|
9484
|
+
id: typeof message.id === "string" ? `rig_steer_${message.id}` : `rig_steer_${Date.now()}`,
|
|
9485
|
+
type: "prompt",
|
|
9486
|
+
message: text,
|
|
9487
|
+
streamingBehavior: "steer"
|
|
9488
|
+
});
|
|
9489
|
+
emitWrapperEvent("pi.rpc.steering.delivered", {
|
|
9490
|
+
runId: input.runId ?? null,
|
|
9491
|
+
steeringId: typeof message.id === "string" ? message.id : null,
|
|
9492
|
+
actor: typeof message.actor === "string" ? message.actor : "operator",
|
|
9493
|
+
message: text
|
|
9494
|
+
});
|
|
9495
|
+
}
|
|
9496
|
+
} catch (error) {
|
|
9497
|
+
emitWrapperEvent("pi.rpc.steering.poll.failed", {
|
|
9498
|
+
runId: input.runId ?? null,
|
|
9499
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9500
|
+
});
|
|
9501
|
+
}
|
|
9502
|
+
await sleep(1000);
|
|
9503
|
+
}
|
|
9504
|
+
};
|
|
9505
|
+
const stdoutPump = pumpReadableLines(proc.stdout, (line) => {
|
|
9506
|
+
stdout.write(`${line}
|
|
9507
|
+
`);
|
|
9508
|
+
const record = parseJsonRecord(line.trim());
|
|
9509
|
+
if (!record)
|
|
9510
|
+
return;
|
|
9511
|
+
if (record.type === "agent_end") {
|
|
9512
|
+
sawAgentEnd = true;
|
|
9513
|
+
closeStdin();
|
|
9514
|
+
return;
|
|
9515
|
+
}
|
|
9516
|
+
if (record.type === "response" && record.command === "prompt" && record.success === false) {
|
|
9517
|
+
promptError = typeof record.error === "string" ? record.error : "Pi RPC prompt failed.";
|
|
9518
|
+
closeStdin();
|
|
9519
|
+
return;
|
|
9520
|
+
}
|
|
9521
|
+
if (isBlockingPiRpcUiRequest(record)) {
|
|
9522
|
+
const id = typeof record.id === "string" ? record.id : "";
|
|
9523
|
+
if (id) {
|
|
9524
|
+
send({ type: "extension_ui_response", id, cancelled: true });
|
|
9525
|
+
emitWrapperEvent("pi.rpc.extension_ui.cancelled", {
|
|
9526
|
+
id,
|
|
9527
|
+
method: record.method,
|
|
9528
|
+
reason: "noninteractive Rig worker RPC session"
|
|
9529
|
+
});
|
|
9530
|
+
}
|
|
9531
|
+
}
|
|
9532
|
+
});
|
|
9533
|
+
const stderrPump = pumpReadableLines(proc.stderr, (line) => {
|
|
9534
|
+
stderr.write(`${line}
|
|
9535
|
+
`);
|
|
9536
|
+
});
|
|
9537
|
+
if (input.sessionName?.trim()) {
|
|
9538
|
+
send({ id: "rig_set_session_name", type: "set_session_name", name: input.sessionName.trim() });
|
|
9539
|
+
}
|
|
9540
|
+
const steeringPollPromise = pollSteering();
|
|
9541
|
+
if (input.prompt.trim()) {
|
|
9542
|
+
send({ id: "rig_initial_prompt", type: "prompt", message: input.prompt });
|
|
9543
|
+
emitWrapperEvent("pi.rpc.prompt.sent", {
|
|
9544
|
+
runId: input.runId ?? null,
|
|
9545
|
+
kind: "initial",
|
|
9546
|
+
bytes: Buffer.byteLength(input.prompt)
|
|
9547
|
+
});
|
|
9548
|
+
} else {
|
|
9549
|
+
closeStdin();
|
|
9550
|
+
}
|
|
9551
|
+
const exitCode = await proc.exited;
|
|
9552
|
+
process.off("SIGTERM", forwardSigterm);
|
|
9553
|
+
steeringPollStopped = true;
|
|
9554
|
+
await Promise.all([stdoutPump, stderrPump, steeringPollPromise]);
|
|
9555
|
+
if (promptError) {
|
|
9556
|
+
stderr.write(`[rig-agent] Pi RPC prompt failed: ${promptError}
|
|
9557
|
+
`);
|
|
9558
|
+
return exitCode === 0 ? 1 : exitCode;
|
|
9559
|
+
}
|
|
9560
|
+
if (input.prompt.trim() && !sawAgentEnd && exitCode === 0) {
|
|
9561
|
+
stderr.write(`[rig-agent] Pi RPC exited before emitting agent_end.
|
|
9562
|
+
`);
|
|
9563
|
+
return 1;
|
|
9564
|
+
}
|
|
9565
|
+
return exitCode;
|
|
9566
|
+
}
|
|
9344
9567
|
function resolveFinalProviderExitCode(input) {
|
|
9345
9568
|
if (input.providerExitCode !== 0) {
|
|
9346
9569
|
return input.providerExitCode;
|
|
@@ -9384,6 +9607,12 @@ function buildProviderArgs(provider, runtime, argv) {
|
|
|
9384
9607
|
piArgs.unshift(normalizePiModelForProvider(model, piProvider));
|
|
9385
9608
|
piArgs.unshift("--model");
|
|
9386
9609
|
}
|
|
9610
|
+
if (!hasCliOption(piArgs, "--mode")) {
|
|
9611
|
+
piArgs.push("--mode", process.env.RIG_PI_TRANSPORT?.trim() === "print" ? "json" : "rpc");
|
|
9612
|
+
if (process.env.RIG_PI_TRANSPORT?.trim() === "print" && !hasCliOption(piArgs, "--print")) {
|
|
9613
|
+
piArgs.push("--print");
|
|
9614
|
+
}
|
|
9615
|
+
}
|
|
9387
9616
|
return piArgs;
|
|
9388
9617
|
}
|
|
9389
9618
|
return [
|
|
@@ -9481,17 +9710,38 @@ function normalizePiModelForProvider(model, provider) {
|
|
|
9481
9710
|
}
|
|
9482
9711
|
return model;
|
|
9483
9712
|
}
|
|
9713
|
+
function resolveFromShellPath(binary) {
|
|
9714
|
+
const resolved = Bun.spawnSync(["sh", "-lc", `command -v ${binary}`], {
|
|
9715
|
+
stdout: "pipe",
|
|
9716
|
+
stderr: "ignore",
|
|
9717
|
+
stdin: "ignore",
|
|
9718
|
+
env: process.env
|
|
9719
|
+
});
|
|
9720
|
+
if (resolved.exitCode !== 0)
|
|
9721
|
+
return null;
|
|
9722
|
+
const path = resolved.stdout.toString().trim().split(/\r?\n/)[0]?.trim();
|
|
9723
|
+
return path || null;
|
|
9724
|
+
}
|
|
9725
|
+
function resolveBundledPiBinary() {
|
|
9726
|
+
try {
|
|
9727
|
+
const packageJson = requireFromRuntime.resolve("@earendil-works/pi-coding-agent/package.json");
|
|
9728
|
+
const binaryPath = resolve35(packageJson, "..", "dist", "cli.js");
|
|
9729
|
+
return existsSync33(binaryPath) ? binaryPath : null;
|
|
9730
|
+
} catch {
|
|
9731
|
+
return null;
|
|
9732
|
+
}
|
|
9733
|
+
}
|
|
9484
9734
|
function providerBinary(provider) {
|
|
9485
9735
|
if (provider === "codex") {
|
|
9486
|
-
return Bun.which("codex") || "codex";
|
|
9736
|
+
return resolveFromShellPath("codex") || Bun.which("codex") || "codex";
|
|
9487
9737
|
}
|
|
9488
9738
|
if (provider === "pi") {
|
|
9489
|
-
return Bun.which("pi") || "pi";
|
|
9739
|
+
return process.env.RIG_PI_BINARY?.trim() || resolveBundledPiBinary() || resolveFromShellPath("pi") || Bun.which("pi") || "pi";
|
|
9490
9740
|
}
|
|
9491
9741
|
try {
|
|
9492
9742
|
return resolveClaudeBinaryPath();
|
|
9493
9743
|
} catch {
|
|
9494
|
-
return Bun.which("claude") || "claude";
|
|
9744
|
+
return resolveFromShellPath("claude") || Bun.which("claude") || "claude";
|
|
9495
9745
|
}
|
|
9496
9746
|
}
|
|
9497
9747
|
function emitWrapperEvent(type, payload) {
|
|
@@ -9726,6 +9976,7 @@ export {
|
|
|
9726
9976
|
updateTaskSourceAfterRun,
|
|
9727
9977
|
startOptionalRuntimeSnapshotSidecar,
|
|
9728
9978
|
shouldBypassProviderSandboxOnPlatform,
|
|
9979
|
+
runPiRpcProvider,
|
|
9729
9980
|
runAgentWrapper,
|
|
9730
9981
|
resolveTaskFromBeads,
|
|
9731
9982
|
resolveFinalProviderExitCode,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/runtime",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -63,12 +63,13 @@
|
|
|
63
63
|
"main": "./dist/src/index.js",
|
|
64
64
|
"module": "./dist/src/index.js",
|
|
65
65
|
"dependencies": {
|
|
66
|
+
"@earendil-works/pi-coding-agent": "^0.75.5",
|
|
66
67
|
"@libsql/client": "^0.17.2",
|
|
67
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
68
|
-
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.
|
|
69
|
-
"@rig/hook-kit": "npm:@h-rig/hook-kit@0.0.6-alpha.
|
|
70
|
-
"@rig/shared": "npm:@h-rig/shared@0.0.6-alpha.
|
|
71
|
-
"@rig/validator-kit": "npm:@h-rig/validator-kit@0.0.6-alpha.
|
|
68
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.22",
|
|
69
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.22",
|
|
70
|
+
"@rig/hook-kit": "npm:@h-rig/hook-kit@0.0.6-alpha.22",
|
|
71
|
+
"@rig/shared": "npm:@h-rig/shared@0.0.6-alpha.22",
|
|
72
|
+
"@rig/validator-kit": "npm:@h-rig/validator-kit@0.0.6-alpha.22",
|
|
72
73
|
"effect": "4.0.0-beta.78",
|
|
73
74
|
"smol-toml": "^1.6.0"
|
|
74
75
|
}
|