@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.61

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.
Files changed (52) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/rig.js +5033 -1895
  3. package/dist/src/commands/_authority-runs.js +2 -3
  4. package/dist/src/commands/_cli-format.js +369 -0
  5. package/dist/src/commands/_connection-state.js +12 -6
  6. package/dist/src/commands/_doctor-checks.js +79 -34
  7. package/dist/src/commands/_help-catalog.js +446 -0
  8. package/dist/src/commands/_operator-surface.js +220 -0
  9. package/dist/src/commands/_operator-view.js +1124 -64
  10. package/dist/src/commands/_parsers.js +0 -2
  11. package/dist/src/commands/_pi-frontend.js +1080 -0
  12. package/dist/src/commands/_pi-install.js +4 -3
  13. package/dist/src/commands/_pi-remote-session.js +771 -0
  14. package/dist/src/commands/_pi-worker-bridge-extension.js +834 -0
  15. package/dist/src/commands/_policy.js +0 -2
  16. package/dist/src/commands/_preflight.js +98 -116
  17. package/dist/src/commands/_run-driver-helpers.js +46 -19
  18. package/dist/src/commands/_run-replay.js +142 -0
  19. package/dist/src/commands/_server-client.js +225 -48
  20. package/dist/src/commands/_snapshot-upload.js +74 -30
  21. package/dist/src/commands/_spinner.js +63 -0
  22. package/dist/src/commands/_task-picker.js +44 -16
  23. package/dist/src/commands/agent.js +10 -12
  24. package/dist/src/commands/browser.js +4 -6
  25. package/dist/src/commands/connect.js +134 -26
  26. package/dist/src/commands/dist.js +4 -6
  27. package/dist/src/commands/doctor.js +79 -34
  28. package/dist/src/commands/github.js +76 -32
  29. package/dist/src/commands/inbox.js +410 -31
  30. package/dist/src/commands/init.js +398 -90
  31. package/dist/src/commands/inspect.js +296 -23
  32. package/dist/src/commands/inspector.js +2 -4
  33. package/dist/src/commands/pi.js +168 -0
  34. package/dist/src/commands/plugin.js +81 -22
  35. package/dist/src/commands/profile-and-review.js +8 -10
  36. package/dist/src/commands/queue.js +2 -3
  37. package/dist/src/commands/remote.js +18 -20
  38. package/dist/src/commands/repo-git-harness.js +6 -8
  39. package/dist/src/commands/run.js +1600 -131
  40. package/dist/src/commands/server.js +281 -42
  41. package/dist/src/commands/setup.js +84 -45
  42. package/dist/src/commands/task-report-bug.js +5 -7
  43. package/dist/src/commands/task-run-driver.js +736 -93
  44. package/dist/src/commands/task.js +1863 -262
  45. package/dist/src/commands/test.js +3 -5
  46. package/dist/src/commands/workspace.js +4 -6
  47. package/dist/src/commands.js +5675 -2531
  48. package/dist/src/index.js +5654 -2519
  49. package/dist/src/launcher.js +5 -3
  50. package/dist/src/report-bug.js +3 -3
  51. package/dist/src/runner.js +5 -19
  52. package/package.json +7 -5
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/task-run-driver.ts
3
- import { copyFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, statSync, writeFileSync } from "fs";
4
- import { resolve as resolve4 } from "path";
3
+ import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync2 } from "fs";
4
+ import { resolve as resolve6 } from "path";
5
5
  import { spawn, spawnSync } from "child_process";
6
6
  import { createInterface as createLineInterface } from "readline";
7
7
 
@@ -9,8 +9,6 @@ import { createInterface as createLineInterface } from "readline";
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
10
  import { CliError } from "@rig/runtime/control-plane/errors";
11
11
  import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
12
- import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
13
- import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
14
12
  import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
15
13
  import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
16
14
  function formatCommand(parts) {
@@ -39,14 +37,14 @@ import {
39
37
  isCodexExecRecord
40
38
  } from "@rig/runtime/control-plane/provider/codex-exec-records";
41
39
  import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
42
- import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
40
+ import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
43
41
  import {
44
- buildTaskRunLifecycleComment,
45
- updateConfiguredTaskSourceTask
42
+ buildTaskRunLifecycleComment
46
43
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
47
44
  import {
48
45
  closeIssueAfterMergedPr,
49
46
  commitRunChanges,
47
+ pushBranchSyncedWithOrigin,
50
48
  runPrAutomation
51
49
  } from "@rig/runtime/control-plane/native/pr-automation";
52
50
 
@@ -54,12 +52,16 @@ import {
54
52
  import { readFileSync } from "fs";
55
53
  import { resolve as resolve3 } from "path";
56
54
  import {
57
- appendJsonlRecord,
55
+ appendRunLifecycleEvent,
56
+ appendRunLogEntry,
57
+ appendRunTimelineEntry,
58
+ patchAuthorityRunRecord,
58
59
  readAuthorityRun as readAuthorityRun2,
59
- resolveAuthorityRunDir as resolveAuthorityRunDir2,
60
- writeJsonFile as writeJsonFile2
60
+ resolveAuthorityRunDir,
61
+ writeJsonFile
61
62
  } from "@rig/runtime/control-plane/authority-files";
62
63
  import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
64
+ import { readPriorPrProgressPromptSection } from "@rig/runtime/control-plane/prompt-context";
63
65
  import { buildProviderTaskRunInstructionLines } from "@rig/runtime/control-plane/provider/runtime-instructions";
64
66
  import { loadRigTaskRunSkillBody } from "@rig/runtime/control-plane/provider/rig-task-run-skill";
65
67
 
@@ -69,8 +71,7 @@ import { resolve as resolve2 } from "path";
69
71
  import {
70
72
  readAuthorityRun,
71
73
  readJsonlFile,
72
- resolveAuthorityRunDir,
73
- writeJsonFile
74
+ writeAuthorityRunRecord
74
75
  } from "@rig/runtime/control-plane/authority-files";
75
76
 
76
77
  // packages/cli/src/commands/_paths.ts
@@ -168,22 +169,16 @@ function upsertAgentAuthorityRun(projectRoot, input) {
168
169
  } else if ("errorText" in next) {
169
170
  delete next.errorText;
170
171
  }
171
- writeJsonFile(resolve2(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
172
+ writeAuthorityRunRecord(projectRoot, input.runId, next);
172
173
  return next;
173
174
  }
174
175
 
175
176
  // packages/cli/src/commands/_run-driver-helpers.ts
176
177
  function patchAuthorityRun(projectRoot, runId, patch) {
177
- const current = readAuthorityRun2(projectRoot, runId);
178
- if (!current) {
178
+ const next = patchAuthorityRunRecord(projectRoot, runId, patch);
179
+ if (!next) {
179
180
  throw new CliError2(`Run not found: ${runId}`, 2);
180
181
  }
181
- const next = {
182
- ...current,
183
- ...patch,
184
- updatedAt: new Date().toISOString()
185
- };
186
- writeJsonFile2(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), next);
187
182
  return next;
188
183
  }
189
184
  function touchAuthorityRun(projectRoot, runId) {
@@ -191,21 +186,21 @@ function touchAuthorityRun(projectRoot, runId) {
191
186
  if (!current) {
192
187
  return;
193
188
  }
194
- writeJsonFile2(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), {
189
+ writeJsonFile(resolve3(resolveAuthorityRunDir(projectRoot, runId), "run.json"), {
195
190
  ...current,
196
191
  updatedAt: new Date().toISOString()
197
192
  });
198
193
  }
199
194
  function appendRunTimeline(projectRoot, runId, value) {
200
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "timeline.jsonl"), value);
195
+ appendRunTimelineEntry(projectRoot, runId, value);
201
196
  touchAuthorityRun(projectRoot, runId);
202
197
  }
203
198
  function appendRunLog(projectRoot, runId, value) {
204
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "logs.jsonl"), value);
199
+ appendRunLogEntry(projectRoot, runId, value);
205
200
  touchAuthorityRun(projectRoot, runId);
206
201
  }
207
202
  function appendRunAction(projectRoot, runId, value) {
208
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "timeline.jsonl"), {
203
+ appendRunTimelineEntry(projectRoot, runId, {
209
204
  id: value.id,
210
205
  type: "action",
211
206
  actionType: value.actionType,
@@ -259,8 +254,38 @@ function startRunAction(projectRoot, runId, input) {
259
254
  }
260
255
  };
261
256
  }
257
+ var runEventSinkProjectRoot = null;
258
+ function configureRunEventSink(projectRoot) {
259
+ runEventSinkProjectRoot = projectRoot;
260
+ }
261
+ var lastDoorbellRangAt = 0;
262
+ function ringServerRunDoorbell(runId) {
263
+ const serverUrl = process.env.RIG_SERVER_URL?.trim();
264
+ if (!serverUrl)
265
+ return;
266
+ const now = Date.now();
267
+ if (now - lastDoorbellRangAt < 250)
268
+ return;
269
+ lastDoorbellRangAt = now;
270
+ const token = process.env.RIG_AUTH_TOKEN || process.env.RIG_GITHUB_TOKEN || "";
271
+ fetch(`${serverUrl.replace(/\/+$/, "")}/api/runs/doorbell`, {
272
+ method: "POST",
273
+ headers: {
274
+ "content-type": "application/json",
275
+ authorization: `Bearer ${token}`
276
+ },
277
+ body: JSON.stringify({ runId }),
278
+ signal: AbortSignal.timeout(1000)
279
+ }).catch(() => {});
280
+ }
262
281
  function emitServerRunEvent(event) {
263
282
  console.log(`__RIG_RUN_EVENT__${JSON.stringify({ ...event, at: new Date().toISOString() })}`);
283
+ if (runEventSinkProjectRoot) {
284
+ try {
285
+ appendRunLifecycleEvent(runEventSinkProjectRoot, event.runId, { ...event });
286
+ } catch {}
287
+ ringServerRunDoorbell(event.runId);
288
+ }
264
289
  }
265
290
  function buildRunPrompt(input) {
266
291
  const initialPrompt = input.initialPrompt?.trim() || null;
@@ -317,6 +342,7 @@ ${acceptance}` : null,
317
342
  ${sourceContractLines.join(`
318
343
  `)}` : null,
319
344
  scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
345
+ readPriorPrProgressPromptSection(input.projectRoot, input.taskId),
320
346
  initialPrompt ? `Additional operator guidance:
321
347
  ${initialPrompt}` : null,
322
348
  providerLines.length > 0 ? `Provider-specific runtime tooling:
@@ -388,6 +414,253 @@ function renderSourceScopeValidation(task, validation) {
388
414
  `);
389
415
  }
390
416
 
417
+ // packages/cli/src/commands/_server-client.ts
418
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
419
+ import { resolve as resolve5 } from "path";
420
+ import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
421
+
422
+ // packages/cli/src/commands/_connection-state.ts
423
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
424
+ import { homedir } from "os";
425
+ import { dirname, resolve as resolve4 } from "path";
426
+ function resolveGlobalConnectionsPath(env = process.env) {
427
+ const explicit = env.RIG_CONNECTIONS_FILE?.trim();
428
+ if (explicit)
429
+ return resolve4(explicit);
430
+ const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
431
+ if (stateDir)
432
+ return resolve4(stateDir, "connections.json");
433
+ return resolve4(homedir(), ".rig", "connections.json");
434
+ }
435
+ function resolveRepoConnectionPath(projectRoot) {
436
+ return resolve4(projectRoot, ".rig", "state", "connection.json");
437
+ }
438
+ function readJsonFile(path) {
439
+ if (!existsSync2(path))
440
+ return null;
441
+ try {
442
+ return JSON.parse(readFileSync2(path, "utf8"));
443
+ } catch (error) {
444
+ throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
445
+ }
446
+ }
447
+ function writeJsonFile2(path, value) {
448
+ mkdirSync(dirname(path), { recursive: true });
449
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
450
+ `, "utf8");
451
+ }
452
+ function normalizeConnection(value) {
453
+ if (!value || typeof value !== "object" || Array.isArray(value))
454
+ return null;
455
+ const record = value;
456
+ if (record.kind === "local")
457
+ return { kind: "local", mode: "auto" };
458
+ if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
459
+ const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
460
+ return { kind: "remote", baseUrl };
461
+ }
462
+ return null;
463
+ }
464
+ function readGlobalConnections(options = {}) {
465
+ const path = resolveGlobalConnectionsPath(options.env ?? process.env);
466
+ const payload = readJsonFile(path);
467
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
468
+ return { connections: {} };
469
+ }
470
+ const rawConnections = payload.connections;
471
+ const connections = {};
472
+ if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
473
+ for (const [alias, raw] of Object.entries(rawConnections)) {
474
+ const connection = normalizeConnection(raw);
475
+ if (connection)
476
+ connections[alias] = connection;
477
+ }
478
+ }
479
+ return { connections };
480
+ }
481
+ function readRepoConnection(projectRoot) {
482
+ const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
483
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
484
+ return null;
485
+ const record = payload;
486
+ const selected = typeof record.selected === "string" ? record.selected.trim() : "";
487
+ if (!selected)
488
+ return null;
489
+ return {
490
+ selected,
491
+ project: typeof record.project === "string" ? record.project : undefined,
492
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
493
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
494
+ };
495
+ }
496
+ function writeRepoConnection(projectRoot, state) {
497
+ writeJsonFile2(resolveRepoConnectionPath(projectRoot), state);
498
+ }
499
+ function resolveSelectedConnection(projectRoot, options = {}) {
500
+ const repo = readRepoConnection(projectRoot);
501
+ if (!repo)
502
+ return null;
503
+ if (repo.selected === "local")
504
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
505
+ const global = readGlobalConnections(options);
506
+ const connection = global.connections[repo.selected];
507
+ if (!connection) {
508
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
509
+ }
510
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
511
+ }
512
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
513
+ const repo = readRepoConnection(projectRoot);
514
+ if (!repo)
515
+ return;
516
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
517
+ }
518
+
519
+ // packages/cli/src/commands/_server-client.ts
520
+ var scopedGitHubBearerTokens = new Map;
521
+ function cleanToken(value) {
522
+ const trimmed = value?.trim();
523
+ return trimmed ? trimmed : null;
524
+ }
525
+ function readPrivateRemoteSessionToken(projectRoot) {
526
+ const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
527
+ if (!existsSync3(path))
528
+ return null;
529
+ try {
530
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
531
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ function readGitHubBearerTokenForRemote(projectRoot) {
537
+ const scopedKey = resolve5(projectRoot);
538
+ if (scopedGitHubBearerTokens.has(scopedKey))
539
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
540
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
541
+ if (privateSession)
542
+ return privateSession;
543
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
544
+ }
545
+ function readStoredGitHubAuthToken(projectRoot) {
546
+ const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
547
+ if (!existsSync3(path))
548
+ return null;
549
+ try {
550
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
551
+ return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
552
+ } catch {
553
+ return null;
554
+ }
555
+ }
556
+ function readLocalConnectionFallbackToken(projectRoot) {
557
+ return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
558
+ }
559
+ async function ensureServerForCli(projectRoot) {
560
+ try {
561
+ const selected = resolveSelectedConnection(projectRoot);
562
+ if (selected?.connection.kind === "remote") {
563
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
564
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
565
+ return {
566
+ baseUrl: selected.connection.baseUrl,
567
+ authToken,
568
+ connectionKind: "remote",
569
+ serverProjectRoot
570
+ };
571
+ }
572
+ const connection = await ensureLocalRigServerConnection(projectRoot);
573
+ return {
574
+ baseUrl: connection.baseUrl,
575
+ authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
576
+ connectionKind: "local",
577
+ serverProjectRoot: resolve5(projectRoot)
578
+ };
579
+ } catch (error) {
580
+ if (error instanceof Error) {
581
+ throw new CliError2(error.message, 1);
582
+ }
583
+ throw error;
584
+ }
585
+ }
586
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
587
+ const repo = readRepoConnection(projectRoot);
588
+ const slug = repo?.project?.trim();
589
+ if (!slug)
590
+ return null;
591
+ try {
592
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
593
+ headers: mergeHeaders(undefined, authToken)
594
+ });
595
+ if (!response.ok)
596
+ return null;
597
+ const payload = await response.json();
598
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
599
+ const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
600
+ const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
601
+ const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
602
+ if (path)
603
+ writeRepoServerProjectRoot(projectRoot, path);
604
+ return path;
605
+ } catch {
606
+ return null;
607
+ }
608
+ }
609
+ function mergeHeaders(headers, authToken) {
610
+ const merged = new Headers(headers);
611
+ if (authToken) {
612
+ merged.set("authorization", `Bearer ${authToken}`);
613
+ }
614
+ return merged;
615
+ }
616
+ function diagnosticMessage(payload) {
617
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
618
+ return null;
619
+ const record = payload;
620
+ const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
621
+ const messages = diagnostics.flatMap((entry) => {
622
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
623
+ return [];
624
+ const diagnostic = entry;
625
+ const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
626
+ const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
627
+ return message ? [`${kind}: ${message}`] : [];
628
+ });
629
+ return messages.length > 0 ? messages.join("; ") : null;
630
+ }
631
+ async function requestServerJson(context, pathname, init = {}) {
632
+ const server = await ensureServerForCli(context.projectRoot);
633
+ const headers = mergeHeaders(init.headers, server.authToken);
634
+ if (server.serverProjectRoot)
635
+ headers.set("x-rig-project-root", server.serverProjectRoot);
636
+ const response = await fetch(`${server.baseUrl}${pathname}`, {
637
+ ...init,
638
+ headers
639
+ });
640
+ const text = await response.text();
641
+ const payload = text.trim().length > 0 ? (() => {
642
+ try {
643
+ return JSON.parse(text);
644
+ } catch {
645
+ return null;
646
+ }
647
+ })() : null;
648
+ if (!response.ok) {
649
+ const diagnostics = diagnosticMessage(payload);
650
+ const detail = diagnostics ?? (text || response.statusText);
651
+ throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
652
+ }
653
+ return payload;
654
+ }
655
+ async function updateWorkspaceTaskViaServer(context, input) {
656
+ const payload = await requestServerJson(context, "/api/tasks/update", {
657
+ method: "POST",
658
+ headers: { "content-type": "application/json" },
659
+ body: JSON.stringify(input)
660
+ });
661
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
662
+ }
663
+
391
664
  // packages/cli/src/commands/task-run-driver.ts
392
665
  var PI_CANONICAL_RUN_STAGES = [
393
666
  "Connect",
@@ -431,6 +704,7 @@ function buildPiRigBridgeEnv(input) {
431
704
  RIG_SERVER_RUN_ID: input.runId,
432
705
  RIG_TASK_ID: input.taskId,
433
706
  RIG_RUNTIME_ADAPTER: "pi",
707
+ RIG_STEERING_POLL_MS: "0",
434
708
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
435
709
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
436
710
  ...githubBridgeEnv(githubToken)
@@ -455,12 +729,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
455
729
  return 0;
456
730
  let copied = 0;
457
731
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
458
- const sourcePath = resolve4(sourceRoot, relativePath);
459
- const targetPath = resolve4(targetRoot, relativePath);
732
+ const sourcePath = resolve6(sourceRoot, relativePath);
733
+ const targetPath = resolve6(targetRoot, relativePath);
460
734
  try {
461
735
  if (!statSync(sourcePath).isFile())
462
736
  continue;
463
- mkdirSync(resolve4(targetPath, ".."), { recursive: true });
737
+ mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
464
738
  copyFileSync(sourcePath, targetPath);
465
739
  copied += 1;
466
740
  } catch {}
@@ -499,7 +773,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
499
773
  return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
500
774
  return {
501
775
  RIG_BASELINE_MODE: "dirty-snapshot",
502
- RIG_DIRTY_BASELINE_READY_FILE: resolve4(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
776
+ RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
503
777
  };
504
778
  }
505
779
  function positiveInt(value, fallback) {
@@ -507,7 +781,7 @@ function positiveInt(value, fallback) {
507
781
  }
508
782
  function resolveTaskRunAutomationLimits(config, env = process.env) {
509
783
  const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
510
- const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 30);
784
+ const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
511
785
  return {
512
786
  maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
513
787
  maxPrFixIterations: configuredPrFixIterations
@@ -601,12 +875,6 @@ function appendPiStageLog(input) {
601
875
  });
602
876
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
603
877
  }
604
- async function runCheckedCommand(command, args, cwd, label = "git") {
605
- const result = await command(args, { cwd });
606
- if (result.exitCode !== 0) {
607
- throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
608
- }
609
- }
610
878
  function createCommandRunner(binary) {
611
879
  return async (args, options) => {
612
880
  const child = spawn(binary, [...args], {
@@ -617,9 +885,9 @@ function createCommandRunner(binary) {
617
885
  const stderrChunks = [];
618
886
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
619
887
  child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
620
- return await new Promise((resolve5) => {
621
- child.once("error", (error) => resolve5({ exitCode: 1, stderr: error.message }));
622
- child.once("close", (code) => resolve5({
888
+ return await new Promise((resolve7) => {
889
+ child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
890
+ child.once("close", (code) => resolve7({
623
891
  exitCode: code ?? 1,
624
892
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
625
893
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -643,8 +911,9 @@ async function runTaskRunPostValidationLifecycle(input) {
643
911
  if (!taskId) {
644
912
  return { status: "skipped" };
645
913
  }
646
- const config = input.config ?? {};
647
- const prMode = config.pr?.mode ?? "auto";
914
+ const configInput = input.config ?? null;
915
+ const config = configInput ?? {};
916
+ const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
648
917
  if (prMode === "off" || prMode === "ask") {
649
918
  input.appendStage?.("Open PR", prMode === "off" ? "PR automation disabled by pr.mode=off." : "PR creation awaiting operator approval by pr.mode=ask.", "skipped", "info");
650
919
  input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
@@ -660,7 +929,7 @@ async function runTaskRunPostValidationLifecycle(input) {
660
929
  gitCommand
661
930
  });
662
931
  const prAutomation = input.prAutomation ?? runPrAutomation;
663
- const updateTaskSource = input.updateTaskSource ?? updateConfiguredTaskSourceTask;
932
+ const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
664
933
  const stage = input.appendStage ?? (() => {
665
934
  return;
666
935
  });
@@ -682,7 +951,7 @@ async function runTaskRunPostValidationLifecycle(input) {
682
951
  command: gitCommand
683
952
  });
684
953
  stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
685
- await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
954
+ await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
686
955
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
687
956
  const pr = await prAutomation({
688
957
  projectRoot: workspace,
@@ -692,7 +961,9 @@ async function runTaskRunPostValidationLifecycle(input) {
692
961
  config,
693
962
  sourceTask: input.sourceTask,
694
963
  uploadedSnapshot: input.uploadedSnapshot,
964
+ artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
695
965
  command: ghCommand,
966
+ gitCommand,
696
967
  steerPi,
697
968
  lifecycle: {
698
969
  onPrOpened: async ({ prUrl }) => {
@@ -741,8 +1012,6 @@ async function runTaskRunPostValidationLifecycle(input) {
741
1012
  runtimeWorkspace: input.runtimeWorkspace ?? null
742
1013
  })
743
1014
  }
744
- }).catch(() => {
745
- return;
746
1015
  });
747
1016
  }
748
1017
  },
@@ -761,8 +1030,6 @@ async function runTaskRunPostValidationLifecycle(input) {
761
1030
  runtimeWorkspace: input.runtimeWorkspace ?? null
762
1031
  })
763
1032
  }
764
- }).catch(() => {
765
- return;
766
1033
  });
767
1034
  }
768
1035
  },
@@ -791,8 +1058,17 @@ async function runTaskRunPostValidationLifecycle(input) {
791
1058
  errorText: detail
792
1059
  })
793
1060
  }
794
- }).catch(() => {
795
- return;
1061
+ }).catch((error) => {
1062
+ try {
1063
+ appendRunLog(input.projectRoot, input.runId, {
1064
+ id: `log:${input.runId}:task-source-needs-attention-update`,
1065
+ title: "Task source needs-attention update failed",
1066
+ detail: error instanceof Error ? error.message : String(error),
1067
+ tone: "error",
1068
+ status: "needs_attention",
1069
+ createdAt: new Date().toISOString()
1070
+ });
1071
+ } catch {}
796
1072
  });
797
1073
  }
798
1074
  return { status: "needs_attention", pr };
@@ -815,7 +1091,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
815
1091
  return null;
816
1092
  }
817
1093
  for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
818
- const summary = readJsonFile(resolve4(artifactDir, "validation-summary.json"), null);
1094
+ const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
819
1095
  if (!summary || summary.status !== "fail") {
820
1096
  continue;
821
1097
  }
@@ -896,14 +1172,14 @@ function readTaskRunAcceptedArtifactState(input) {
896
1172
  if (!input.taskId || !input.workspaceDir) {
897
1173
  return { accepted: false, reason: null };
898
1174
  }
899
- const artifactDir = resolve4(input.workspaceDir, "artifacts", input.taskId);
900
- const reviewStatusPath = resolve4(artifactDir, "review-status.txt");
901
- const taskResultPath = resolve4(artifactDir, "task-result.json");
1175
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1176
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1177
+ const taskResultPath = resolve6(artifactDir, "task-result.json");
902
1178
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
903
1179
  if (reviewStatus !== "APPROVED") {
904
1180
  return { accepted: false, reason: null };
905
1181
  }
906
- const taskResult = readJsonFile(taskResultPath, null);
1182
+ const taskResult = readJsonFile2(taskResultPath, null);
907
1183
  if (taskResult?.status !== "completed") {
908
1184
  return { accepted: false, reason: null };
909
1185
  }
@@ -935,13 +1211,13 @@ function resolveTaskRunRetryContext(input) {
935
1211
  if (!input.taskId || !input.workspaceDir) {
936
1212
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
937
1213
  }
938
- const artifactDir = resolve4(input.workspaceDir, "artifacts", input.taskId);
939
- const reviewStatePath = resolve4(artifactDir, "review-state.json");
940
- const reviewFeedbackPath = resolve4(artifactDir, "review-feedback.md");
941
- const reviewStatusPath = resolve4(artifactDir, "review-status.txt");
942
- const failedApproachesPath = resolve4(input.workspaceDir, ".rig", "state", "failed_approaches.md");
943
- const validationSummaryPath = resolve4(artifactDir, "validation-summary.json");
944
- const reviewState = readJsonFile(reviewStatePath, null);
1214
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1215
+ const reviewStatePath = resolve6(artifactDir, "review-state.json");
1216
+ const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
1217
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1218
+ const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
1219
+ const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
1220
+ const reviewState = readJsonFile2(reviewStatePath, null);
945
1221
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
946
1222
  const reviewRejected = isTaskRunReviewRejected(reviewState);
947
1223
  if (reviewStatus === "APPROVED") {
@@ -994,12 +1270,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
994
1270
  }
995
1271
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
996
1272
  }
1273
+ function appendAssistantTimelineFromRecord(input) {
1274
+ let nextAssistantText = input.assistantText;
1275
+ if (input.record.type === "message_update") {
1276
+ const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
1277
+ if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
1278
+ nextAssistantText += assistantMessageEvent.delta;
1279
+ }
1280
+ } else if (input.record.type === "stream_event") {
1281
+ const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
1282
+ const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
1283
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
1284
+ nextAssistantText += delta.text;
1285
+ }
1286
+ } else if (input.record.type === "assistant") {
1287
+ const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
1288
+ const content = Array.isArray(message.content) ? message.content : [];
1289
+ const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
1290
+ if (fullText.length > nextAssistantText.length)
1291
+ nextAssistantText = fullText;
1292
+ }
1293
+ if (nextAssistantText !== input.assistantText) {
1294
+ appendRunTimeline(input.projectRoot, input.runId, {
1295
+ id: input.messageId,
1296
+ type: "assistant_message",
1297
+ text: nextAssistantText,
1298
+ state: "streaming",
1299
+ createdAt: new Date().toISOString()
1300
+ });
1301
+ }
1302
+ return nextAssistantText;
1303
+ }
1304
+ function appendPiRpcProtocolLogFromRecord(input) {
1305
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1306
+ if (type === "response") {
1307
+ const command = typeof input.record.command === "string" ? input.record.command : "rpc";
1308
+ const success = input.record.success !== false;
1309
+ if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
1310
+ return true;
1311
+ }
1312
+ appendRunLog(input.projectRoot, input.runId, {
1313
+ id: input.nextRunLogId(),
1314
+ title: success ? "Pi RPC response" : "Pi RPC error",
1315
+ detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
1316
+ tone: success ? "tool" : "error",
1317
+ status: input.status,
1318
+ payload: input.record,
1319
+ createdAt: new Date().toISOString()
1320
+ });
1321
+ emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
1322
+ return true;
1323
+ }
1324
+ if (type !== "extension_ui_request")
1325
+ return false;
1326
+ const method = typeof input.record.method === "string" ? input.record.method : "ui";
1327
+ let title = "Pi UI event";
1328
+ let detail = method;
1329
+ let tone = "info";
1330
+ if (method === "notify") {
1331
+ title = "Pi notification";
1332
+ detail = String(input.record.message ?? "");
1333
+ tone = input.record.notifyType === "error" ? "error" : "info";
1334
+ } else if (method === "setStatus") {
1335
+ title = "Pi UI status";
1336
+ detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
1337
+ tone = "tool";
1338
+ } else if (method === "setWidget") {
1339
+ title = "Pi UI widget";
1340
+ const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
1341
+ detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
1342
+ tone = "tool";
1343
+ } else if (method === "setTitle") {
1344
+ title = "Pi UI title";
1345
+ detail = String(input.record.title ?? "");
1346
+ tone = "tool";
1347
+ } else if (method === "set_editor_text") {
1348
+ title = "Pi editor update";
1349
+ detail = String(input.record.text ?? "").slice(0, 500);
1350
+ tone = "tool";
1351
+ } else {
1352
+ title = "Pi UI request";
1353
+ detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
1354
+ }
1355
+ appendRunLog(input.projectRoot, input.runId, {
1356
+ id: input.nextRunLogId(),
1357
+ title,
1358
+ detail,
1359
+ tone,
1360
+ status: input.status,
1361
+ payload: input.record,
1362
+ createdAt: new Date().toISOString()
1363
+ });
1364
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1365
+ return true;
1366
+ }
1367
+ function appendPiToolTimelineFromRecord(input) {
1368
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1369
+ if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
1370
+ return false;
1371
+ const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
1372
+ const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
1373
+ const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
1374
+ appendRunTimeline(input.projectRoot, input.runId, {
1375
+ id: `tool:${toolCallId}:${type}`,
1376
+ type,
1377
+ toolName,
1378
+ status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
1379
+ createdAt: new Date().toISOString()
1380
+ });
1381
+ return true;
1382
+ }
1383
+ function isNonRenderablePiProtocolRecord(record) {
1384
+ const type = typeof record.type === "string" ? record.type : "";
1385
+ 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");
1386
+ }
1387
+ function appendToolTimelineFromLog(input) {
1388
+ const title = typeof input.log.title === "string" ? input.log.title : "";
1389
+ if (title !== "Tool activity")
1390
+ return;
1391
+ const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
1392
+ const toolName = typeof payload.toolName === "string" && payload.toolName.trim() ? payload.toolName.trim() : typeof payload.tool_name === "string" && payload.tool_name.trim() ? payload.tool_name.trim() : null;
1393
+ const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
1394
+ appendRunTimeline(input.projectRoot, input.runId, {
1395
+ id: `tool:${logId}`,
1396
+ type: "tool_execution_update",
1397
+ toolName,
1398
+ status: typeof input.log.status === "string" ? input.log.status : "running",
1399
+ detail: typeof input.log.detail === "string" ? input.log.detail : null,
1400
+ payload,
1401
+ createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
1402
+ });
1403
+ }
997
1404
  function readTaskRunReviewStatus(reviewStatusPath) {
998
- if (!existsSync2(reviewStatusPath)) {
1405
+ if (!existsSync4(reviewStatusPath)) {
999
1406
  return null;
1000
1407
  }
1001
1408
  try {
1002
- const status = readFileSync2(reviewStatusPath, "utf8").trim().toUpperCase();
1409
+ const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
1003
1410
  return status === "APPROVED" || status === "REJECTED" ? status : null;
1004
1411
  } catch {
1005
1412
  return null;
@@ -1017,7 +1424,38 @@ function isTaskRunReviewRejected(reviewState) {
1017
1424
  function runSourceTaskIdentity(sourceTask) {
1018
1425
  return sourceTask;
1019
1426
  }
1020
- async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateConfiguredTaskSourceTask) {
1427
+ function sourceTaskIssueNodeId(sourceTask) {
1428
+ if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
1429
+ return null;
1430
+ const record = sourceTask;
1431
+ const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
1432
+ if (direct?.trim())
1433
+ return direct.trim();
1434
+ const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
1435
+ return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
1436
+ }
1437
+ var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
1438
+ const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
1439
+ id: input.taskId,
1440
+ ...input.update.status ? { status: input.update.status } : {},
1441
+ ...input.update.comment ? { comment: input.update.comment } : {},
1442
+ ...input.update.title ? { title: input.update.title } : {},
1443
+ ...typeof input.update.body === "string" ? { body: input.update.body } : {},
1444
+ issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
1445
+ });
1446
+ if (serverResult.ok === false) {
1447
+ throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
1448
+ }
1449
+ return {
1450
+ updated: serverResult.ok !== false,
1451
+ taskId: input.taskId,
1452
+ status: input.update.status,
1453
+ source: "server",
1454
+ sourceKind: "server",
1455
+ projectSync: serverResult.projectSync
1456
+ };
1457
+ };
1458
+ async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
1021
1459
  if (!taskId)
1022
1460
  return;
1023
1461
  const config = await loadTaskRunAutomationConfig(projectRoot);
@@ -1081,9 +1519,14 @@ function stringArrayField(record, key) {
1081
1519
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
1082
1520
  }
1083
1521
  async function executeRigOwnedTaskRun(context, input) {
1522
+ process.stdout.on("error", () => {});
1523
+ process.stderr.on("error", () => {});
1524
+ configureRunEventSink(context.projectRoot);
1084
1525
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
1085
1526
  const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
1086
1527
  const resumeMode = process.env.RIG_RUN_RESUME === "1";
1528
+ let latestPiSessionId = typeof existingRunRecord?.piSession?.sessionId === "string" && existingRunRecord.piSession.sessionId.trim() ? existingRunRecord.piSession.sessionId : null;
1529
+ const sessionAnchor = () => latestPiSessionId ? { sessionId: latestPiSessionId } : {};
1087
1530
  const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
1088
1531
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
1089
1532
  let prompt = buildRunPrompt({
@@ -1107,11 +1550,7 @@ async function executeRigOwnedTaskRun(context, input) {
1107
1550
  ...input.model ? ["--model", input.model] : [],
1108
1551
  "--prompt"
1109
1552
  ] : input.runtimeAdapter === "pi" ? [
1110
- "--print",
1111
- "--verbose",
1112
- "--mode",
1113
- "json",
1114
- "--no-session",
1553
+ "__rig_pi_session_daemon__",
1115
1554
  ...input.model ? ["--model", input.model] : []
1116
1555
  ] : [
1117
1556
  "--print",
@@ -1160,7 +1599,8 @@ async function executeRigOwnedTaskRun(context, input) {
1160
1599
  type: "status",
1161
1600
  runId: input.runId,
1162
1601
  status: "preparing",
1163
- detail: input.taskId ?? input.title ?? runtimeTaskId
1602
+ detail: input.taskId ?? input.title ?? runtimeTaskId,
1603
+ ...sessionAnchor()
1164
1604
  });
1165
1605
  appendRunLog(context.projectRoot, input.runId, {
1166
1606
  id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
@@ -1186,15 +1626,15 @@ async function executeRigOwnedTaskRun(context, input) {
1186
1626
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
1187
1627
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
1188
1628
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
1189
- const planningArtifactPath = resolve4("artifacts", runtimeTaskId, "implementation-plan.md");
1629
+ const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
1190
1630
  const persistedPlanning = {
1191
1631
  ...planningClassification,
1192
1632
  classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
1193
1633
  artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
1194
1634
  classifiedAt: new Date().toISOString()
1195
1635
  };
1196
- mkdirSync(resolve4(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
1197
- writeFileSync(resolve4(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
1636
+ mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
1637
+ writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
1198
1638
  `, "utf8");
1199
1639
  patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
1200
1640
  prompt = `${prompt}
@@ -1208,7 +1648,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1208
1648
  projectRoot: context.projectRoot,
1209
1649
  runId: input.runId,
1210
1650
  stage,
1211
- detail: stage === "Launch Pi" ? "Pi runtime bridge starting with pi-rig environment." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running." : null,
1651
+ 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,
1212
1652
  status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
1213
1653
  });
1214
1654
  }
@@ -1244,11 +1684,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1244
1684
  let verificationStarted = false;
1245
1685
  let reviewStarted = false;
1246
1686
  let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
1247
- let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve4(existingRunRecord.sessionPath, "..") : null;
1687
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
1248
1688
  let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
1249
1689
  let latestProviderCommand = null;
1250
1690
  let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
1251
1691
  let snapshotSidecarPromise = null;
1692
+ let wrapperManagesRuntimeSnapshot = false;
1252
1693
  let dirtyBaselineApplied = false;
1253
1694
  const childEnv = {
1254
1695
  ...process.env,
@@ -1280,7 +1721,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1280
1721
  return;
1281
1722
  verificationStarted = true;
1282
1723
  patchAuthorityRun(context.projectRoot, input.runId, { status: "validating" });
1283
- emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null });
1724
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null, ...sessionAnchor() });
1284
1725
  verificationAction = startRunAction(context.projectRoot, input.runId, {
1285
1726
  actionId: `action:${input.runId}:completion-verification`,
1286
1727
  actionType: "completion-verification",
@@ -1293,7 +1734,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1293
1734
  return;
1294
1735
  reviewStarted = true;
1295
1736
  patchAuthorityRun(context.projectRoot, input.runId, { status: "reviewing" });
1296
- emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null });
1737
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null, ...sessionAnchor() });
1297
1738
  reviewAction = startRunAction(context.projectRoot, input.runId, {
1298
1739
  actionId: `action:${input.runId}:review-closeout`,
1299
1740
  actionType: "run.review",
@@ -1301,6 +1742,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1301
1742
  detail: detail ?? "Verifier review is running."
1302
1743
  });
1303
1744
  };
1745
+ const nextRunLogId = createRunLogIdFactory(input.runId);
1304
1746
  const handleWrapperEvent = (rawPayload) => {
1305
1747
  let event = null;
1306
1748
  try {
@@ -1316,6 +1758,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1316
1758
  latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
1317
1759
  latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
1318
1760
  const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
1761
+ wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
1319
1762
  latestRuntimeBranch = runtimeId;
1320
1763
  provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
1321
1764
  runtimeId: runtimeId ?? null,
@@ -1327,10 +1770,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1327
1770
  patchAuthorityRun(context.projectRoot, input.runId, {
1328
1771
  status: "running",
1329
1772
  worktreePath: latestRuntimeWorkspace,
1330
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve4(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1773
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1331
1774
  logRoot: latestLogsDir,
1332
- sessionPath: latestSessionDir ? resolve4(latestSessionDir, "session.json") : null,
1333
- sessionLogPath: latestLogsDir ? resolve4(latestLogsDir, "agent-stdout.log") : null,
1775
+ sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
1776
+ sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
1334
1777
  branch: runtimeId
1335
1778
  });
1336
1779
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
@@ -1338,8 +1781,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1338
1781
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
1339
1782
  const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
1340
1783
  if (readyFile) {
1341
- mkdirSync(resolve4(readyFile, ".."), { recursive: true });
1342
- writeFileSync(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1784
+ mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
1785
+ writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1343
1786
  `, "utf8");
1344
1787
  }
1345
1788
  appendRunLog(context.projectRoot, input.runId, {
@@ -1353,7 +1796,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1353
1796
  });
1354
1797
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
1355
1798
  }
1356
- if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1799
+ if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1357
1800
  snapshotSidecarPromise = (async () => {
1358
1801
  const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
1359
1802
  projectRoot: context.projectRoot,
@@ -1381,7 +1824,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1381
1824
  type: "status",
1382
1825
  runId: input.runId,
1383
1826
  status: "running",
1384
- detail: latestRuntimeWorkspace
1827
+ detail: latestRuntimeWorkspace,
1828
+ ...sessionAnchor()
1385
1829
  });
1386
1830
  return true;
1387
1831
  }
@@ -1435,9 +1879,71 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1435
1879
  }
1436
1880
  return true;
1437
1881
  }
1882
+ 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") {
1883
+ 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";
1884
+ 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");
1885
+ appendRunLog(context.projectRoot, input.runId, {
1886
+ id: nextRunLogId(),
1887
+ title,
1888
+ detail,
1889
+ tone: event.type === "pi.session.error" ? "error" : "info",
1890
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
1891
+ payload: {
1892
+ eventType: event.type,
1893
+ runId: typeof payload.runId === "string" ? payload.runId : null,
1894
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
1895
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
1896
+ },
1897
+ createdAt: new Date().toISOString()
1898
+ });
1899
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1900
+ return true;
1901
+ }
1902
+ if (event.type === "pi.session.ready") {
1903
+ const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
1904
+ const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
1905
+ if (privateMetadata) {
1906
+ patchAuthorityRun(context.projectRoot, input.runId, {
1907
+ piSession: publicMetadata,
1908
+ piSessionPrivate: privateMetadata
1909
+ });
1910
+ const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
1911
+ if (sessionId) {
1912
+ latestPiSessionId = sessionId;
1913
+ }
1914
+ appendRunLog(context.projectRoot, input.runId, {
1915
+ id: `log:${input.runId}:pi-session-ready`,
1916
+ title: "Worker Pi session ready",
1917
+ detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
1918
+ tone: "info",
1919
+ status: "running",
1920
+ payload: {
1921
+ sessionId: sessionId || null,
1922
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
1923
+ },
1924
+ createdAt: new Date().toISOString()
1925
+ });
1926
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
1927
+ }
1928
+ return true;
1929
+ }
1930
+ 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") {
1931
+ 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";
1932
+ 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")}`;
1933
+ appendRunLog(context.projectRoot, input.runId, {
1934
+ id: nextRunLogId(),
1935
+ title,
1936
+ detail,
1937
+ tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
1938
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
1939
+ payload,
1940
+ createdAt: new Date().toISOString()
1941
+ });
1942
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1943
+ return true;
1944
+ }
1438
1945
  return false;
1439
1946
  };
1440
- const nextRunLogId = createRunLogIdFactory(input.runId);
1441
1947
  const handleAgentStdoutLine = (line) => {
1442
1948
  const trimmed = line.trim();
1443
1949
  if (!trimmed)
@@ -1471,6 +1977,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1471
1977
  try {
1472
1978
  const record = JSON.parse(trimmed);
1473
1979
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
1980
+ if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
1981
+ projectRoot: context.projectRoot,
1982
+ runId: input.runId,
1983
+ record,
1984
+ status: liveLogStatus,
1985
+ nextRunLogId
1986
+ })) {
1987
+ return;
1988
+ }
1989
+ if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
1990
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1991
+ return;
1992
+ }
1474
1993
  const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
1475
1994
  runId: input.runId,
1476
1995
  record,
@@ -1487,7 +2006,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1487
2006
  if (providerLogs.length > 0) {
1488
2007
  for (const providerLog of providerLogs) {
1489
2008
  appendRunLog(context.projectRoot, input.runId, providerLog);
2009
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
1490
2010
  emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2011
+ if (providerLog.title === "Tool activity")
2012
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1491
2013
  }
1492
2014
  }
1493
2015
  if (input.runtimeAdapter === "codex") {
@@ -1539,6 +2061,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1539
2061
  return;
1540
2062
  }
1541
2063
  }
2064
+ if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
2065
+ return;
2066
+ }
1542
2067
  if (record.type === "assistant") {
1543
2068
  const message = record.message && typeof record.message === "object" ? record.message : record;
1544
2069
  const content = Array.isArray(message.content) ? message.content : [];
@@ -1645,7 +2170,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1645
2170
  let acceptedArtifactObservedAt = null;
1646
2171
  let acceptedArtifactPollTimer = null;
1647
2172
  let acceptedArtifactKillTimer = null;
1648
- const attemptExit = await new Promise((resolve5) => {
2173
+ const attemptExit = await new Promise((resolve7) => {
1649
2174
  let settled = false;
1650
2175
  const settle = (result) => {
1651
2176
  if (settled)
@@ -1653,7 +2178,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1653
2178
  settled = true;
1654
2179
  if (acceptedArtifactPollTimer)
1655
2180
  clearInterval(acceptedArtifactPollTimer);
1656
- resolve5(result);
2181
+ resolve7(result);
1657
2182
  };
1658
2183
  const pollAcceptedArtifacts = () => {
1659
2184
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -1726,7 +2251,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1726
2251
  });
1727
2252
  for (const pendingLog of pendingLogs) {
1728
2253
  appendRunLog(context.projectRoot, input.runId, pendingLog);
2254
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
1729
2255
  emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2256
+ if (pendingLog.title === "Tool activity")
2257
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1730
2258
  }
1731
2259
  process.off("SIGTERM", forwardSigterm);
1732
2260
  if (attemptExit.error) {
@@ -1777,7 +2305,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1777
2305
  type: "status",
1778
2306
  runId: input.runId,
1779
2307
  status: "validating",
1780
- detail: failureDetail
2308
+ detail: failureDetail,
2309
+ ...sessionAnchor()
1781
2310
  });
1782
2311
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Validate failed" });
1783
2312
  emitServerRunEvent({ type: "log", runId: input.runId, title: `Steering Pi to retry validation (attempt ${attempt + 1}/${maxAttempts})` });
@@ -1852,8 +2381,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1852
2381
  }
1853
2382
  if (planningClassification.planningRequired) {
1854
2383
  const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
1855
- const expectedPlanPath = resolve4(planWorkspace, planningArtifactPath);
1856
- if (!existsSync2(expectedPlanPath)) {
2384
+ const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
2385
+ if (!existsSync4(expectedPlanPath)) {
1857
2386
  const failedAt = new Date().toISOString();
1858
2387
  const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
1859
2388
  patchAuthorityRun(context.projectRoot, input.runId, {
@@ -1873,6 +2402,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1873
2402
  throw new CliError2(failureDetail, 1);
1874
2403
  }
1875
2404
  }
2405
+ if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
2406
+ appendPiStageLog({
2407
+ projectRoot: context.projectRoot,
2408
+ runId: input.runId,
2409
+ stage: "Validate",
2410
+ detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
2411
+ status: "completed"
2412
+ });
2413
+ if (verificationAction && !reviewStarted) {
2414
+ verificationAction.complete("Completion verification checks finished.");
2415
+ }
2416
+ if (!reviewAction) {
2417
+ promoteToReviewing("Server-owned closeout is queued.");
2418
+ }
2419
+ if (reviewAction) {
2420
+ reviewAction.complete("Provider work accepted; server-owned closeout requested.");
2421
+ }
2422
+ const requestedAt = new Date().toISOString();
2423
+ patchAuthorityRun(context.projectRoot, input.runId, {
2424
+ status: "reviewing",
2425
+ completedAt: null,
2426
+ errorText: null,
2427
+ serverCloseout: {
2428
+ status: "pending",
2429
+ phase: "queued",
2430
+ requestedAt,
2431
+ updatedAt: requestedAt,
2432
+ runtimeWorkspace: latestRuntimeWorkspace,
2433
+ branch: latestRuntimeBranch,
2434
+ taskId: input.taskId ?? runtimeTaskId
2435
+ }
2436
+ });
2437
+ appendRunLog(context.projectRoot, input.runId, {
2438
+ id: `log:${input.runId}:server-closeout-requested`,
2439
+ title: "Server-owned closeout requested",
2440
+ detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
2441
+ tone: "info",
2442
+ status: "reviewing",
2443
+ createdAt: requestedAt,
2444
+ payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
2445
+ });
2446
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
2447
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested.", ...sessionAnchor() });
2448
+ await context.emitEvent("command.finished", {
2449
+ command: [
2450
+ "rig",
2451
+ "server",
2452
+ "task-run",
2453
+ ...input.taskId ? ["--task", input.taskId] : [],
2454
+ ...input.title ? ["--title", input.title] : []
2455
+ ],
2456
+ formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
2457
+ exitCode: 0,
2458
+ durationMs: 0,
2459
+ startedAt,
2460
+ finishedAt: requestedAt
2461
+ });
2462
+ process.exit(0);
2463
+ }
1876
2464
  const runPiPrFeedbackFix = async (message) => {
1877
2465
  appendPiStageLog({
1878
2466
  projectRoot: context.projectRoot,
@@ -1900,11 +2488,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1900
2488
  child.stdin.write(message);
1901
2489
  }
1902
2490
  child.stdin.end();
2491
+ const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
2492
+ let feedbackAssistantText = "";
2493
+ const feedbackPendingToolUses = new Map;
1903
2494
  const stdout = createLineInterface({ input: child.stdout });
1904
2495
  stdout.on("line", (line) => {
1905
2496
  const trimmed = line.trim();
1906
2497
  if (!trimmed)
1907
2498
  return;
2499
+ try {
2500
+ const record = JSON.parse(trimmed);
2501
+ const providerLogs = buildClaudeLogsFromRecord({
2502
+ runId: input.runId,
2503
+ record,
2504
+ createdAtFallback: new Date().toISOString(),
2505
+ status: "reviewing",
2506
+ pendingToolUses: feedbackPendingToolUses
2507
+ });
2508
+ for (const providerLog of providerLogs) {
2509
+ appendRunLog(context.projectRoot, input.runId, providerLog);
2510
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
2511
+ emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2512
+ if (providerLog.title === "Tool activity")
2513
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2514
+ }
2515
+ const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
2516
+ projectRoot: context.projectRoot,
2517
+ runId: input.runId,
2518
+ messageId: feedbackAssistantMessageId,
2519
+ record,
2520
+ assistantText: feedbackAssistantText
2521
+ });
2522
+ const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
2523
+ if (hadAssistantDelta) {
2524
+ feedbackAssistantText = nextFeedbackAssistantText;
2525
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2526
+ }
2527
+ if (providerLogs.length > 0 || hadAssistantDelta)
2528
+ return;
2529
+ } catch {}
1908
2530
  appendRunLog(context.projectRoot, input.runId, {
1909
2531
  id: nextRunLogId(),
1910
2532
  title: "Pi PR feedback fix output",
@@ -1930,10 +2552,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1930
2552
  });
1931
2553
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
1932
2554
  });
1933
- const exitCode = await new Promise((resolve5) => {
1934
- child.once("error", () => resolve5(1));
1935
- child.once("close", (code) => resolve5(code ?? 1));
2555
+ const exitCode = await new Promise((resolve7) => {
2556
+ child.once("error", () => resolve7(1));
2557
+ child.once("close", (code) => resolve7(code ?? 1));
1936
2558
  });
2559
+ for (const pendingLog of flushPendingClaudeToolUseLogs({
2560
+ runId: input.runId,
2561
+ status: exitCode === 0 ? "completed" : "failed",
2562
+ pendingToolUses: feedbackPendingToolUses
2563
+ })) {
2564
+ appendRunLog(context.projectRoot, input.runId, pendingLog);
2565
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
2566
+ emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2567
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2568
+ }
2569
+ if (feedbackAssistantText.trim()) {
2570
+ appendRunTimeline(context.projectRoot, input.runId, {
2571
+ id: feedbackAssistantMessageId,
2572
+ type: "assistant_message",
2573
+ text: feedbackAssistantText,
2574
+ state: "completed",
2575
+ createdAt: new Date().toISOString(),
2576
+ completedAt: new Date().toISOString()
2577
+ });
2578
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2579
+ }
1937
2580
  if (exitCode !== 0) {
1938
2581
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
1939
2582
  }