@h-rig/server 0.0.6-alpha.22 → 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/src/index.js CHANGED
@@ -1530,14 +1530,29 @@ function summarizeUsefulRunError(projectRoot, runId, fallback) {
1530
1530
  const nonGeneric = errorLines.at(-1);
1531
1531
  return nonGeneric ?? (typeof fallback === "string" ? fallback : null);
1532
1532
  }
1533
+ function readRunPiSessionMetadata(projectRoot, runId) {
1534
+ const run = readAuthorityRun(projectRoot, runId);
1535
+ const metadata = run?.piSessionPrivate;
1536
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
1537
+ return null;
1538
+ const record = metadata;
1539
+ const publicMetadata = record.public;
1540
+ const daemonConnection = record.daemonConnection;
1541
+ if (!publicMetadata || typeof publicMetadata !== "object" || Array.isArray(publicMetadata))
1542
+ return null;
1543
+ if (!daemonConnection || typeof daemonConnection !== "object" || Array.isArray(daemonConnection))
1544
+ return null;
1545
+ return metadata;
1546
+ }
1533
1547
  function readRunDetails(projectRoot, runId) {
1534
1548
  const run = readAuthorityRun(projectRoot, runId);
1535
1549
  if (!run) {
1536
1550
  return null;
1537
1551
  }
1538
1552
  const usefulErrorText = isGenericRunFailure(run.errorText) ? summarizeUsefulRunError(projectRoot, runId, run.errorText) : null;
1553
+ const { piSessionPrivate: _piSessionPrivate, ...publicRun } = run;
1539
1554
  return {
1540
- run: usefulErrorText ? { ...run, errorText: usefulErrorText } : run,
1555
+ run: usefulErrorText ? { ...publicRun, errorText: usefulErrorText } : publicRun,
1541
1556
  timeline: readJsonlFile(resolve4(resolveAuthorityRunDir(projectRoot, runId), "timeline.jsonl")),
1542
1557
  approvals: readApprovals(projectRoot, { runId }),
1543
1558
  userInputs: readUserInputs(projectRoot, { runId })
@@ -3244,6 +3259,12 @@ function patchRunRecord(projectRoot, runId, patch) {
3244
3259
  writeJsonFile2(resolve11(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), next);
3245
3260
  return next;
3246
3261
  }
3262
+ function patchRunPiSessionMetadata(projectRoot, runId, metadata) {
3263
+ return patchRunRecord(projectRoot, runId, {
3264
+ piSession: metadata?.public ?? null,
3265
+ piSessionPrivate: metadata
3266
+ });
3267
+ }
3247
3268
  function buildRunStartPatch(startedAt) {
3248
3269
  return {
3249
3270
  status: "preparing",
@@ -4435,10 +4456,10 @@ function closeoutPhasePatch(phase, status, extra = {}) {
4435
4456
  const updatedAt = new Date().toISOString();
4436
4457
  return {
4437
4458
  serverCloseout: {
4459
+ ...extra,
4438
4460
  phase,
4439
4461
  status,
4440
- updatedAt,
4441
- ...extra
4462
+ updatedAt
4442
4463
  }
4443
4464
  };
4444
4465
  }
@@ -4556,6 +4577,58 @@ async function updateRunTaskSourceLifecycle(projectRoot, run, status, summary, o
4556
4577
  });
4557
4578
  }
4558
4579
  }
4580
+ async function markServerOwnedCloseoutFailed(state, runId, error) {
4581
+ const detail = error instanceof Error ? error.message : String(error);
4582
+ const current = readAuthorityRun4(state.projectRoot, runId);
4583
+ patchRunRecord(state.projectRoot, runId, {
4584
+ status: "failed",
4585
+ completedAt: new Date().toISOString(),
4586
+ errorText: detail,
4587
+ ...closeoutPhasePatch("failed", "failed", { error: detail })
4588
+ });
4589
+ appendRunLogEntryAndBroadcast(state, runId, {
4590
+ id: `log:${runId}:server-closeout-failed`,
4591
+ title: "Server-owned closeout failed",
4592
+ detail,
4593
+ tone: "error",
4594
+ status: "failed",
4595
+ createdAt: new Date().toISOString()
4596
+ }, "server-closeout-failed");
4597
+ if (current?.taskId) {
4598
+ await updateRunTaskSourceLifecycle(state.projectRoot, { ...current, status: "failed", errorText: detail }, "failed", "Rig server-owned closeout failed.", { errorText: detail }).catch((sourceError) => {
4599
+ appendRunLogEntry(state.projectRoot, runId, {
4600
+ id: `log:${runId}:task-source-closeout-failed-update`,
4601
+ title: "Task source closeout failure update failed",
4602
+ detail: sourceError instanceof Error ? sourceError.message : String(sourceError),
4603
+ tone: "error",
4604
+ status: "failed",
4605
+ createdAt: new Date().toISOString()
4606
+ });
4607
+ });
4608
+ }
4609
+ }
4610
+ function scheduleServerOwnedPrCloseout(state, runId, reason) {
4611
+ const startedAt = new Date().toISOString();
4612
+ state.runProcesses.set(runId, {
4613
+ runId,
4614
+ child: null,
4615
+ startedAt,
4616
+ stopped: false
4617
+ });
4618
+ queueMicrotask(() => {
4619
+ withServerAuthorityEnvIfNeeded(state.projectRoot, async () => {
4620
+ try {
4621
+ await runServerOwnedPrCloseout(state, runId);
4622
+ } catch (error) {
4623
+ await markServerOwnedCloseoutFailed(state, runId, error);
4624
+ } finally {
4625
+ state.runProcesses.delete(runId);
4626
+ broadcastSnapshotInvalidation(state, `server-closeout-${reason}-terminal`);
4627
+ await reconcileScheduler(state, `server-closeout-${reason}-terminal`);
4628
+ }
4629
+ });
4630
+ });
4631
+ }
4559
4632
  async function runServerOwnedPrCloseout(state, runId) {
4560
4633
  const run = readAuthorityRun4(state.projectRoot, runId);
4561
4634
  if (!run)
@@ -4567,7 +4640,7 @@ async function runServerOwnedPrCloseout(state, runId) {
4567
4640
  if (!taskId)
4568
4641
  throw new Error("Server-owned closeout requires a task id.");
4569
4642
  const workspace = normalizeString(closeout.runtimeWorkspace) ?? normalizeString(run.worktreePath) ?? state.projectRoot;
4570
- const branch = normalizeString(closeout.branch) ?? `rig/${taskId}-${runId}`;
4643
+ let branch = normalizeString(closeout.branch) ?? `rig/${taskId}-${runId}`;
4571
4644
  const config = await loadRigLifecycleConfig(state.projectRoot);
4572
4645
  const runPrMode = normalizeString(run.prMode);
4573
4646
  const prMode = runPrMode === "auto" || runPrMode === "ask" || runPrMode === "off" ? runPrMode : config?.pr?.mode ?? "off";
@@ -4641,6 +4714,12 @@ async function runServerOwnedPrCloseout(state, runId) {
4641
4714
  const githubEnv = githubToken ? { RIG_GITHUB_TOKEN: githubToken, GITHUB_TOKEN: githubToken, GH_TOKEN: githubToken } : {};
4642
4715
  const gitCommand = createCommandRunner("git", githubEnv);
4643
4716
  const ghCommand = createCommandRunner("gh", githubEnv);
4717
+ const workspaceBranch = await gitCommand(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: workspace });
4718
+ const currentWorkspaceBranch = workspaceBranch.exitCode === 0 ? normalizeString(workspaceBranch.stdout) : null;
4719
+ if (currentWorkspaceBranch && currentWorkspaceBranch !== "HEAD" && currentWorkspaceBranch !== branch) {
4720
+ appendCloseoutStage(state, runId, "branch", `Using runtime workspace branch ${currentWorkspaceBranch} instead of recorded branch ${branch}.`, "reviewing", "info");
4721
+ branch = currentWorkspaceBranch;
4722
+ }
4644
4723
  const setCloseout = (phase, status, extra = {}) => {
4645
4724
  const previous = closeoutRecord(readCurrentRun()) ?? closeout;
4646
4725
  patchRunRecord(state.projectRoot, runId, {
@@ -5011,6 +5090,25 @@ async function startLocalRun(state, runId, options) {
5011
5090
  broadcastSnapshotInvalidation(state);
5012
5091
  continue;
5013
5092
  }
5093
+ if (line.startsWith("__RIG_WRAPPER_EVENT__")) {
5094
+ try {
5095
+ const wrapperEvent = JSON.parse(line.slice("__RIG_WRAPPER_EVENT__".length));
5096
+ const eventType = normalizeString(wrapperEvent.type);
5097
+ const payload = wrapperEvent.payload && typeof wrapperEvent.payload === "object" && !Array.isArray(wrapperEvent.payload) ? wrapperEvent.payload : {};
5098
+ if (eventType === "pi.session.ready" && payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata)) {
5099
+ patchRunPiSessionMetadata(state.projectRoot, runId, payload.privateMetadata);
5100
+ }
5101
+ appendRunTimelineEntry(state.projectRoot, runId, {
5102
+ id: `timeline:${runId}:${Date.now()}:wrapper:${eventType ?? "event"}`,
5103
+ type: "wrapper-event",
5104
+ eventType,
5105
+ payload,
5106
+ createdAt: normalizeString(wrapperEvent.at) ?? new Date().toISOString()
5107
+ });
5108
+ } catch {}
5109
+ broadcastSnapshotInvalidation(state, "wrapper-event");
5110
+ continue;
5111
+ }
5014
5112
  appendRunLogEntryAndBroadcast(state, runId, {
5015
5113
  id: `log:${runId}:${Date.now()}`,
5016
5114
  title,
@@ -5039,7 +5137,13 @@ async function startLocalRun(state, runId, options) {
5039
5137
  if (!current) {
5040
5138
  return;
5041
5139
  }
5042
- if (exit.code !== 0 && current.status !== "completed" && current.status !== "stopped") {
5140
+ if (closeoutRecord(current)?.status === "pending") {
5141
+ try {
5142
+ await runServerOwnedPrCloseout(state, runId);
5143
+ } catch (closeoutError) {
5144
+ await markServerOwnedCloseoutFailed(state, runId, closeoutError);
5145
+ }
5146
+ } else if (exit.code !== 0 && current.status !== "completed" && current.status !== "stopped") {
5043
5147
  const completedAt = current.completedAt ?? new Date().toISOString();
5044
5148
  const failureSummary = normalizeString(current.errorText) ?? summarizeRunValidationFailure(state.projectRoot, current) ?? `Rig task-run exited with code ${String(exit.code ?? "unknown")}`;
5045
5149
  if (current.status !== "failed") {
@@ -5074,38 +5178,6 @@ ${sourceFailure}` });
5074
5178
  agent: current.runtimeAdapter,
5075
5179
  summary: failureSummary
5076
5180
  });
5077
- } else if (closeoutRecord(current)?.status === "pending") {
5078
- try {
5079
- await runServerOwnedPrCloseout(state, runId);
5080
- } catch (closeoutError) {
5081
- const closeoutFailure = closeoutError instanceof Error ? closeoutError.message : String(closeoutError);
5082
- patchRunRecord(state.projectRoot, runId, {
5083
- status: "failed",
5084
- completedAt: new Date().toISOString(),
5085
- errorText: closeoutFailure,
5086
- ...closeoutPhasePatch("failed", "failed", { error: closeoutFailure })
5087
- });
5088
- appendRunLogEntryAndBroadcast(state, runId, {
5089
- id: `log:${runId}:server-closeout-failed`,
5090
- title: "Server-owned closeout failed",
5091
- detail: closeoutFailure,
5092
- tone: "error",
5093
- status: "failed",
5094
- createdAt: new Date().toISOString()
5095
- }, "server-closeout-failed");
5096
- if (current.taskId) {
5097
- await updateRunTaskSourceLifecycle(state.projectRoot, { ...current, status: "failed", errorText: closeoutFailure }, "failed", "Rig server-owned closeout failed.", { errorText: closeoutFailure }).catch((error) => {
5098
- appendRunLogEntry(state.projectRoot, runId, {
5099
- id: `log:${runId}:task-source-closeout-failed-update`,
5100
- title: "Task source closeout failure update failed",
5101
- detail: error instanceof Error ? error.message : String(error),
5102
- tone: "error",
5103
- status: "failed",
5104
- createdAt: new Date().toISOString()
5105
- });
5106
- });
5107
- }
5108
- }
5109
5181
  }
5110
5182
  broadcastSnapshotInvalidation(state);
5111
5183
  } catch (error) {
@@ -5194,8 +5266,14 @@ async function resumeRunRecord(state, input) {
5194
5266
  }
5195
5267
  const closeout = closeoutRecord(run);
5196
5268
  const closeoutStatus = normalizeString(closeout?.status)?.toLowerCase() ?? "";
5197
- if (RESUMABLE_SERVER_CLOSEOUT_STATUSES.has(closeoutStatus)) {
5198
- await runServerOwnedPrCloseout(state, input.runId);
5269
+ if (EXPLICIT_RESUMABLE_SERVER_CLOSEOUT_STATUSES.has(closeoutStatus)) {
5270
+ patchRunRecord(state.projectRoot, input.runId, {
5271
+ status: "reviewing",
5272
+ completedAt: null,
5273
+ errorText: null,
5274
+ ...closeoutPhasePatch("queued", "pending", { ...closeout, resumedAt: input.createdAt })
5275
+ });
5276
+ scheduleServerOwnedPrCloseout(state, input.runId, "explicit-resume");
5199
5277
  return;
5200
5278
  }
5201
5279
  await startLocalRun(state, input.runId, { promptOverride: input.promptOverride ?? null, resume: input.restart !== true });
@@ -5283,6 +5361,7 @@ function removeTaskIdsFromQueueState(projectRoot, taskIds) {
5283
5361
  return next;
5284
5362
  }
5285
5363
  var RESUMABLE_SERVER_CLOSEOUT_STATUSES = new Set(["pending", "running"]);
5364
+ var EXPLICIT_RESUMABLE_SERVER_CLOSEOUT_STATUSES = new Set(["pending", "running", "needs_attention"]);
5286
5365
  var ACTIVE_LOCAL_RUN_STATUSES = new Set(["created", "preparing", "running", "validating", "reviewing"]);
5287
5366
  function processExists(pid) {
5288
5367
  if (!Number.isInteger(pid) || pid <= 0)
@@ -5298,7 +5377,23 @@ function recoverStaleLocalRun(projectRoot, run) {
5298
5377
  const record = run;
5299
5378
  if (run.mode !== "local")
5300
5379
  return false;
5380
+ const closeout = closeoutRecord(record);
5381
+ const closeoutStatus = normalizeString(closeout?.status)?.toLowerCase() ?? "";
5301
5382
  const status = normalizeString(record.status)?.toLowerCase() ?? "";
5383
+ if (RESUMABLE_SERVER_CLOSEOUT_STATUSES.has(closeoutStatus))
5384
+ return false;
5385
+ if (closeoutStatus === "needs_attention") {
5386
+ if (!ACTIVE_LOCAL_RUN_STATUSES.has(status))
5387
+ return false;
5388
+ const completedAt2 = record.completedAt ?? new Date().toISOString();
5389
+ patchRunRecord(projectRoot, run.runId, {
5390
+ status: "needs_attention",
5391
+ completedAt: completedAt2,
5392
+ errorText: normalizeString(record.errorText) ?? (Array.isArray(closeout?.feedback) ? closeout.feedback.map(String).join(`
5393
+ `) : null)
5394
+ });
5395
+ return true;
5396
+ }
5302
5397
  if (!ACTIVE_LOCAL_RUN_STATUSES.has(status))
5303
5398
  return false;
5304
5399
  const serverPid = typeof record.serverPid === "number" ? record.serverPid : null;
@@ -5355,15 +5450,7 @@ async function reconcileScheduler(state, reason) {
5355
5450
  status: "reviewing",
5356
5451
  createdAt: new Date().toISOString()
5357
5452
  });
5358
- await runServerOwnedPrCloseout(state, run.runId).catch((error) => {
5359
- const detail = error instanceof Error ? error.message : String(error);
5360
- patchRunRecord(state.projectRoot, run.runId, {
5361
- status: "failed",
5362
- completedAt: new Date().toISOString(),
5363
- errorText: detail,
5364
- ...closeoutPhasePatch("failed", "failed", { error: detail })
5365
- });
5366
- });
5453
+ scheduleServerOwnedPrCloseout(state, run.runId, "auto-resume");
5367
5454
  changed = true;
5368
5455
  }
5369
5456
  if (changed) {
@@ -5445,7 +5532,7 @@ import { basename, dirname as dirname15, isAbsolute as isAbsolute4, resolve as r
5445
5532
  import { copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync12 } from "fs";
5446
5533
  import {
5447
5534
  listAuthorityRuns as listAuthorityRuns5,
5448
- readAuthorityRun as readAuthorityRun6,
5535
+ readAuthorityRun as readAuthorityRun7,
5449
5536
  resolveAuthorityPaths,
5450
5537
  writeJsonFile as writeJsonFile4
5451
5538
  } from "@rig/runtime/control-plane/authority-files";
@@ -5465,10 +5552,64 @@ import {
5465
5552
  RemoteWsClient
5466
5553
  } from "@rig/runtime/control-plane/remote";
5467
5554
 
5555
+ // packages/server/src/server-helpers/pi-session-proxy.ts
5556
+ import { readAuthorityRun as readAuthorityRun5 } from "@rig/runtime/control-plane/authority-files";
5557
+ function resolveRunPiSessionProxy(projectRoot, runId) {
5558
+ const run = readAuthorityRun5(projectRoot, runId);
5559
+ if (!run)
5560
+ return null;
5561
+ const privateMetadata = readRunPiSessionMetadata(projectRoot, runId);
5562
+ if (!privateMetadata)
5563
+ return { pending: true, runId, status: typeof run.status === "string" ? run.status : undefined };
5564
+ const connection = privateMetadata.daemonConnection;
5565
+ if (connection.mode !== "http")
5566
+ throw new Error("Only loopback HTTP Rig Pi session daemon connections are supported");
5567
+ const token = tokenFromRef(connection.tokenRef);
5568
+ if (!token)
5569
+ throw new Error("Rig Pi session daemon token is unavailable");
5570
+ return {
5571
+ runId,
5572
+ sessionId: privateMetadata.public.sessionId,
5573
+ metadata: privateMetadata.public,
5574
+ privateMetadata,
5575
+ baseUrl: connection.baseUrl.replace(/\/+$/, ""),
5576
+ token
5577
+ };
5578
+ }
5579
+ async function proxyRunPiHttp(projectRoot, runId, input) {
5580
+ const resolved = resolveRunPiSessionProxy(projectRoot, runId);
5581
+ if (resolved === null)
5582
+ return { status: 404, payload: { ok: false, error: "Run not found" } };
5583
+ if ("pending" in resolved)
5584
+ return { status: 409, payload: { ready: false, runId, status: resolved.status, retryAfterMs: 500 } };
5585
+ const response = await fetch(`${resolved.baseUrl}${input.daemonPath}`, {
5586
+ method: input.method,
5587
+ headers: {
5588
+ authorization: `Bearer ${resolved.token}`,
5589
+ ...input.body === undefined ? {} : { "content-type": "application/json" }
5590
+ },
5591
+ body: input.body === undefined ? undefined : JSON.stringify(input.body)
5592
+ });
5593
+ const text = await response.text();
5594
+ const payload = text.trim() ? JSON.parse(text) : null;
5595
+ return { status: response.status, payload };
5596
+ }
5597
+ function buildRunPiDaemonWebSocketUrl(resolved) {
5598
+ const url = new URL(`${resolved.baseUrl}/sessions/${encodeURIComponent(resolved.sessionId)}/events`);
5599
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
5600
+ url.searchParams.set("token", resolved.token);
5601
+ return url.toString();
5602
+ }
5603
+ function tokenFromRef(ref) {
5604
+ if (ref.startsWith("inline:"))
5605
+ return ref.slice("inline:".length) || null;
5606
+ return null;
5607
+ }
5608
+
5468
5609
  // packages/server/src/server-helpers/run-steering.ts
5469
5610
  import { dirname as dirname10, resolve as resolve15 } from "path";
5470
5611
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync5 } from "fs";
5471
- import { appendJsonlRecord as appendJsonlRecord2, readAuthorityRun as readAuthorityRun5, resolveAuthorityRunDir as resolveAuthorityRunDir4 } from "@rig/runtime/control-plane/authority-files";
5612
+ import { appendJsonlRecord as appendJsonlRecord2, readAuthorityRun as readAuthorityRun6, resolveAuthorityRunDir as resolveAuthorityRunDir4 } from "@rig/runtime/control-plane/authority-files";
5472
5613
  var steeringSequence = 0;
5473
5614
  function runSteeringPath(projectRoot, runId) {
5474
5615
  return resolve15(resolveAuthorityRunDir4(projectRoot, runId), "steering.jsonl");
@@ -5537,7 +5678,7 @@ function markQueuedRunSteeringMessagesDelivered(projectRoot, runId, ids) {
5537
5678
  return delivered;
5538
5679
  }
5539
5680
  function queueRunSteeringMessage(projectRoot, runId, input) {
5540
- const run = readAuthorityRun5(projectRoot, runId);
5681
+ const run = readAuthorityRun6(projectRoot, runId);
5541
5682
  if (!run)
5542
5683
  throw new Error(`Run not found: ${runId}`);
5543
5684
  const text = input.message.trim();
@@ -6525,7 +6666,8 @@ function isAuthorizedInspectorStreamRequest(req, authToken) {
6525
6666
  }
6526
6667
  function buildDeploymentStatus(projectRoot) {
6527
6668
  const envCommit = normalizeCommit(process.env.RIG_COMMIT_SHA ?? process.env.GITHUB_SHA ?? process.env.VERCEL_GIT_COMMIT_SHA ?? process.env.RAILWAY_GIT_COMMIT_SHA ?? process.env.COMMIT_SHA);
6528
- const gitCommit = envCommit ?? readGitHeadCommit(projectRoot);
6669
+ const deploymentRoot = normalizeString(process.env.RIG_HOST_PROJECT_ROOT) ?? projectRoot;
6670
+ const gitCommit = envCommit ?? readGitHeadCommit(deploymentRoot) ?? readGitHeadCommit(projectRoot);
6529
6671
  return {
6530
6672
  currentCommit: gitCommit,
6531
6673
  commitSource: envCommit ? "env" : gitCommit ? "git" : null,
@@ -7115,7 +7257,7 @@ function redactSecretFields(value) {
7115
7257
  return redacted;
7116
7258
  }
7117
7259
  function validateRemoteLease(deps, state, input) {
7118
- const run = readAuthorityRun6(state.projectRoot, input.runId);
7260
+ const run = readAuthorityRun7(state.projectRoot, input.runId);
7119
7261
  if (!run) {
7120
7262
  return { ok: false, response: deps.jsonResponse({ ok: false, error: "Remote run not found" }, 404) };
7121
7263
  }
@@ -7135,6 +7277,43 @@ function createRigServerFetch(state, deps) {
7135
7277
  return deps.withServerPathEnv(state.projectRoot, async () => {
7136
7278
  const browserOrigin = deps.resolveAllowedBrowserOrigin(req);
7137
7279
  const finalizeResponse = (response) => deps.withCorsHeaders(response, req, browserOrigin);
7280
+ const earlyUrl = new URL(req.url);
7281
+ const piEventsWsMatch = earlyUrl.pathname.match(/^\/api\/runs\/([^/]+)\/pi\/events$/);
7282
+ if (piEventsWsMatch && req.headers.get("upgrade")?.toLowerCase() === "websocket") {
7283
+ const queryToken = earlyUrl.searchParams.get("token");
7284
+ const authHeaders = new Headers(req.headers);
7285
+ if (queryToken && !authHeaders.has("authorization")) {
7286
+ authHeaders.set("authorization", `Bearer ${queryToken}`);
7287
+ }
7288
+ const legacyAuthorized = Boolean(state.authToken && queryToken === state.authToken);
7289
+ const requestAuth = authorizeRigHttpRequest({
7290
+ req: new Request(req.url, { method: req.method, headers: authHeaders }),
7291
+ pathname: earlyUrl.pathname,
7292
+ projectRoot: state.projectRoot,
7293
+ serverAuthToken: state.authToken,
7294
+ legacyAuthorized
7295
+ });
7296
+ if (!requestAuth.authorized) {
7297
+ return deps.jsonResponse({ ok: false, error: "Unauthorized WebSocket connection", reason: requestAuth.reason }, 401);
7298
+ }
7299
+ const runId = decodeURIComponent(piEventsWsMatch[1]);
7300
+ const resolved = resolveRunPiSessionProxy(state.projectRoot, runId);
7301
+ if (resolved === null)
7302
+ return deps.jsonResponse({ ok: false, error: "Run not found" }, 404);
7303
+ if ("pending" in resolved)
7304
+ return deps.jsonResponse({ ready: false, runId, status: resolved.status, retryAfterMs: 500 }, 202);
7305
+ if (!server)
7306
+ return deps.jsonResponse({ ok: false, error: "WebSocket upgrade unavailable" }, 400);
7307
+ const upgraded = server.upgrade(req, {
7308
+ data: {
7309
+ kind: "pi-session-proxy",
7310
+ connectedAt: new Date().toISOString(),
7311
+ upstreamUrl: buildRunPiDaemonWebSocketUrl(resolved),
7312
+ runId
7313
+ }
7314
+ });
7315
+ return upgraded ? new Response(null) : deps.jsonResponse({ ok: false, error: "WebSocket upgrade failed" }, 400);
7316
+ }
7138
7317
  const upgradeResponse = handleWebSocketUpgrade({
7139
7318
  req,
7140
7319
  server,
@@ -8873,6 +9052,49 @@ data: ${JSON.stringify({ connectedAt: new Date().toISOString() })}
8873
9052
  return deps.jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, 404);
8874
9053
  }
8875
9054
  }
9055
+ const runPiMatch = url.pathname.match(/^\/api\/runs\/([^/]+)\/pi(?:\/(.*))?$/);
9056
+ if (runPiMatch) {
9057
+ const runId = decodeURIComponent(runPiMatch[1]);
9058
+ const action = runPiMatch[2] || "";
9059
+ const resolved = resolveRunPiSessionProxy(state.projectRoot, runId);
9060
+ if (resolved === null)
9061
+ return deps.notFound();
9062
+ if ("pending" in resolved) {
9063
+ if (action === "" && req.method === "GET") {
9064
+ return deps.jsonResponse({ ready: false, runId, status: resolved.status, retryAfterMs: 500 }, 202);
9065
+ }
9066
+ return deps.jsonResponse({ ready: false, runId, status: resolved.status, retryAfterMs: 500, error: "Pi session is not ready" }, 409);
9067
+ }
9068
+ if (action === "" && req.method === "GET")
9069
+ return deps.jsonResponse({ ready: true, metadata: resolved.metadata });
9070
+ const body = req.method === "GET" ? undefined : await deps.readJsonBody(req);
9071
+ const sessionPath = `/sessions/${encodeURIComponent(resolved.sessionId)}`;
9072
+ const daemonPath = (() => {
9073
+ if (action === "messages" && req.method === "GET")
9074
+ return `${sessionPath}/messages`;
9075
+ if (action === "status" && req.method === "GET")
9076
+ return `${sessionPath}/status`;
9077
+ if (action === "commands" && req.method === "GET")
9078
+ return `${sessionPath}/commands`;
9079
+ if (action === "prompt" && req.method === "POST")
9080
+ return `${sessionPath}/prompt`;
9081
+ if (action === "shell" && req.method === "POST")
9082
+ return `${sessionPath}/shell`;
9083
+ if (action === "commands/run" && req.method === "POST")
9084
+ return `${sessionPath}/commands/run`;
9085
+ if (action === "commands/respond" && req.method === "POST")
9086
+ return `${sessionPath}/commands/respond`;
9087
+ if (action === "extension-ui/respond" && req.method === "POST")
9088
+ return `${sessionPath}/extension-ui/respond`;
9089
+ if (action === "abort" && req.method === "POST")
9090
+ return `${sessionPath}/abort`;
9091
+ return null;
9092
+ })();
9093
+ if (!daemonPath)
9094
+ return deps.notFound();
9095
+ const proxied = await proxyRunPiHttp(state.projectRoot, runId, { method: req.method, daemonPath, body });
9096
+ return deps.jsonResponse(proxied.payload, proxied.status);
9097
+ }
8876
9098
  const runSteeringAckMatch = url.pathname.match(/^\/api\/runs\/([^/]+)\/steering\/ack$/);
8877
9099
  if (runSteeringAckMatch && req.method === "POST") {
8878
9100
  const runId = decodeURIComponent(runSteeringAckMatch[1]);
@@ -8995,7 +9217,7 @@ import {
8995
9217
  RemoteWsClient as RemoteWsClient2
8996
9218
  } from "@rig/runtime/control-plane/remote";
8997
9219
  import { deleteRunState } from "@rig/runtime/control-plane/native/run-ops";
8998
- import { readAuthorityRun as readAuthorityRun7 } from "@rig/runtime/control-plane/authority-files";
9220
+ import { readAuthorityRun as readAuthorityRun8 } from "@rig/runtime/control-plane/authority-files";
8999
9221
  function redactRemoteEndpoint2(endpoint) {
9000
9222
  const { token, ...rest } = endpoint;
9001
9223
  return {
@@ -9209,7 +9431,7 @@ async function routeWebSocketRequest(state, deps, request) {
9209
9431
  if (!runId || !messageId || text === null) {
9210
9432
  throw new Error("runId, messageId, and text are required");
9211
9433
  }
9212
- const run = readAuthorityRun7(state.projectRoot, runId);
9434
+ const run = readAuthorityRun8(state.projectRoot, runId);
9213
9435
  if (!run) {
9214
9436
  throw new Error(`Run not found: ${runId}`);
9215
9437
  }
@@ -12170,7 +12392,7 @@ import {
12170
12392
  } from "@rig/runtime/control-plane/native/run-ops";
12171
12393
  import {
12172
12394
  listAuthorityRuns as listAuthorityRuns6,
12173
- readAuthorityRun as readAuthorityRun8
12395
+ readAuthorityRun as readAuthorityRun9
12174
12396
  } from "@rig/runtime/control-plane/authority-files";
12175
12397
  function providerFromRuntimeAdapter(runtimeAdapter) {
12176
12398
  if (!runtimeAdapter) {
@@ -12250,7 +12472,7 @@ function discoverInspectorRuns(options) {
12250
12472
  discovered.set(surface.runId, existing);
12251
12473
  };
12252
12474
  for (const authorityEntry of listAuthorityRuns6(options.projectRoot)) {
12253
- const run = readAuthorityRun8(options.projectRoot, authorityEntry.runId);
12475
+ const run = readAuthorityRun9(options.projectRoot, authorityEntry.runId);
12254
12476
  if (!run) {
12255
12477
  continue;
12256
12478
  }
@@ -14250,6 +14472,26 @@ function startPoller(state, pollMs) {
14250
14472
  }
14251
14473
  }, pollMs);
14252
14474
  }
14475
+ function attachPiSessionProxySocket(ws) {
14476
+ if (ws.data.kind !== "pi-session-proxy")
14477
+ return;
14478
+ const upstream = new WebSocket(ws.data.upstreamUrl);
14479
+ ws.__rigPiUpstream = upstream;
14480
+ upstream.addEventListener("message", (event) => {
14481
+ if (ws.readyState === 1)
14482
+ ws.send(typeof event.data === "string" ? event.data : event.data);
14483
+ });
14484
+ upstream.addEventListener("close", () => {
14485
+ try {
14486
+ ws.close();
14487
+ } catch {}
14488
+ });
14489
+ upstream.addEventListener("error", () => {
14490
+ try {
14491
+ ws.close(1011, "Upstream Pi session stream failed");
14492
+ } catch {}
14493
+ });
14494
+ }
14253
14495
  async function createRigServer(options, projectRoot = resolveProjectRoot()) {
14254
14496
  const state = await createRigServerState(projectRoot, options.eventType, options.authToken, {
14255
14497
  upstreamSyncMs: options.upstreamSyncMs,
@@ -14262,10 +14504,20 @@ async function createRigServer(options, projectRoot = resolveProjectRoot()) {
14262
14504
  fetch: (req, server2) => createRigServerFetch2(state)(req, server2),
14263
14505
  websocket: {
14264
14506
  open(ws) {
14507
+ if (ws.data.kind === "pi-session-proxy") {
14508
+ attachPiSessionProxySocket(ws);
14509
+ return;
14510
+ }
14265
14511
  state.sockets.add(ws);
14266
14512
  sendWebSocketResponse(ws, buildServerWelcomePush(state.projectRoot));
14267
14513
  },
14268
14514
  async message(ws, raw) {
14515
+ if (ws.data.kind === "pi-session-proxy") {
14516
+ const upstream = ws.__rigPiUpstream;
14517
+ if (upstream?.readyState === WebSocket.OPEN)
14518
+ upstream.send(raw);
14519
+ return;
14520
+ }
14269
14521
  const request = parseWebSocketRequestEnvelope(typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8"));
14270
14522
  if (!request) {
14271
14523
  sendWebSocketResponse(ws, {
@@ -14288,6 +14540,10 @@ async function createRigServer(options, projectRoot = resolveProjectRoot()) {
14288
14540
  }
14289
14541
  },
14290
14542
  close(ws) {
14543
+ if (ws.data.kind === "pi-session-proxy") {
14544
+ ws.__rigPiUpstream?.close();
14545
+ return;
14546
+ }
14291
14547
  state.sockets.delete(ws);
14292
14548
  }
14293
14549
  }