@h-rig/runtime 0.0.6-alpha.20 → 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
- const proc = Bun.spawn(command, {
9033
- cwd: runtime.workspaceDir,
9034
- env,
9035
- stdin: "inherit",
9036
- stdout: "inherit",
9037
- stderr: "inherit"
9038
- });
9039
- exitCode = await proc.exited;
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
- const proc = Bun.spawn(command, {
9301
- cwd: runtime.workspaceDir,
9302
- env,
9303
- stdin: "inherit",
9304
- stdout: "inherit",
9305
- stderr: "inherit"
9306
- });
9307
- exitCode = await proc.exited;
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.20",
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.20",
68
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.20",
69
- "@rig/hook-kit": "npm:@h-rig/hook-kit@0.0.6-alpha.20",
70
- "@rig/shared": "npm:@h-rig/shared@0.0.6-alpha.20",
71
- "@rig/validator-kit": "npm:@h-rig/validator-kit@0.0.6-alpha.20",
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
  }