@h-rig/cli 0.0.6-alpha.7 → 0.0.6-alpha.71

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 (53) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/rig.js +4507 -1506
  3. package/dist/src/commands/_async-ui.js +152 -0
  4. package/dist/src/commands/_authority-runs.js +2 -3
  5. package/dist/src/commands/_cli-format.js +369 -0
  6. package/dist/src/commands/_connection-state.js +30 -11
  7. package/dist/src/commands/_doctor-checks.js +177 -43
  8. package/dist/src/commands/_help-catalog.js +485 -0
  9. package/dist/src/commands/_json-output.js +56 -0
  10. package/dist/src/commands/_operator-surface.js +220 -0
  11. package/dist/src/commands/_operator-view.js +595 -72
  12. package/dist/src/commands/_parsers.js +18 -11
  13. package/dist/src/commands/_pi-frontend.js +411 -0
  14. package/dist/src/commands/_pi-install.js +4 -3
  15. package/dist/src/commands/_policy.js +12 -5
  16. package/dist/src/commands/_preflight.js +187 -127
  17. package/dist/src/commands/_run-driver-helpers.js +75 -22
  18. package/dist/src/commands/_run-replay.js +142 -0
  19. package/dist/src/commands/_server-client.js +343 -60
  20. package/dist/src/commands/_snapshot-upload.js +160 -38
  21. package/dist/src/commands/_spinner.js +65 -0
  22. package/dist/src/commands/_task-picker.js +44 -16
  23. package/dist/src/commands/agent.js +39 -20
  24. package/dist/src/commands/browser.js +28 -21
  25. package/dist/src/commands/connect.js +146 -33
  26. package/dist/src/commands/dist.js +19 -12
  27. package/dist/src/commands/doctor.js +304 -44
  28. package/dist/src/commands/github.js +301 -52
  29. package/dist/src/commands/inbox.js +679 -72
  30. package/dist/src/commands/init.js +622 -118
  31. package/dist/src/commands/inspect.js +515 -32
  32. package/dist/src/commands/inspector.js +20 -13
  33. package/dist/src/commands/pi.js +177 -0
  34. package/dist/src/commands/plugin.js +95 -27
  35. package/dist/src/commands/profile-and-review.js +26 -19
  36. package/dist/src/commands/queue.js +32 -12
  37. package/dist/src/commands/remote.js +43 -36
  38. package/dist/src/commands/repo-git-harness.js +22 -15
  39. package/dist/src/commands/run.js +1162 -158
  40. package/dist/src/commands/server.js +373 -56
  41. package/dist/src/commands/setup.js +316 -62
  42. package/dist/src/commands/stats.js +1030 -0
  43. package/dist/src/commands/task-report-bug.js +29 -22
  44. package/dist/src/commands/task-run-driver.js +862 -129
  45. package/dist/src/commands/task.js +1423 -311
  46. package/dist/src/commands/test.js +15 -8
  47. package/dist/src/commands/workspace.js +18 -11
  48. package/dist/src/commands.js +4446 -1499
  49. package/dist/src/index.js +4502 -1504
  50. package/dist/src/launcher.js +77 -13
  51. package/dist/src/report-bug.js +3 -3
  52. package/dist/src/runner.js +16 -22
  53. package/package.json +10 -5
@@ -1,18 +1,25 @@
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
 
8
8
  // packages/cli/src/runner.ts
9
9
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
10
- import { CliError } from "@rig/runtime/control-plane/errors";
10
+ import { CliError as RuntimeCliError } 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
- import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
13
+
14
+ class CliError extends RuntimeCliError {
15
+ hint;
16
+ constructor(message, exitCode = 1, options = {}) {
17
+ super(message, exitCode);
18
+ if (options.hint?.trim()) {
19
+ this.hint = options.hint.trim();
20
+ }
21
+ }
22
+ }
16
23
  function formatCommand(parts) {
17
24
  return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
18
25
  }
@@ -39,14 +46,14 @@ import {
39
46
  isCodexExecRecord
40
47
  } from "@rig/runtime/control-plane/provider/codex-exec-records";
41
48
  import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
42
- import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
49
+ import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
43
50
  import {
44
- buildTaskRunLifecycleComment,
45
- updateConfiguredTaskSourceTask
51
+ buildTaskRunLifecycleComment
46
52
  } from "@rig/runtime/control-plane/tasks/source-lifecycle";
47
53
  import {
48
54
  closeIssueAfterMergedPr,
49
55
  commitRunChanges,
56
+ pushBranchSyncedWithOrigin,
50
57
  runPrAutomation
51
58
  } from "@rig/runtime/control-plane/native/pr-automation";
52
59
 
@@ -54,12 +61,17 @@ import {
54
61
  import { readFileSync } from "fs";
55
62
  import { resolve as resolve3 } from "path";
56
63
  import {
57
- appendJsonlRecord,
64
+ appendRunJournalEvent,
65
+ appendRunLogEntry,
66
+ appendRunTimelineEntry,
67
+ patchAuthorityRunRecord,
58
68
  readAuthorityRun as readAuthorityRun2,
59
- resolveAuthorityRunDir as resolveAuthorityRunDir2,
60
- writeJsonFile as writeJsonFile2
69
+ resolveAuthorityRunDir,
70
+ setAuthorityRunStatus,
71
+ writeJsonFile
61
72
  } from "@rig/runtime/control-plane/authority-files";
62
73
  import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
74
+ import { readPriorPrProgressPromptSection } from "@rig/runtime/control-plane/prompt-context";
63
75
  import { buildProviderTaskRunInstructionLines } from "@rig/runtime/control-plane/provider/runtime-instructions";
64
76
  import { loadRigTaskRunSkillBody } from "@rig/runtime/control-plane/provider/rig-task-run-skill";
65
77
 
@@ -69,8 +81,7 @@ import { resolve as resolve2 } from "path";
69
81
  import {
70
82
  readAuthorityRun,
71
83
  readJsonlFile,
72
- resolveAuthorityRunDir,
73
- writeJsonFile
84
+ writeAuthorityRunRecord
74
85
  } from "@rig/runtime/control-plane/authority-files";
75
86
 
76
87
  // packages/cli/src/commands/_paths.ts
@@ -168,22 +179,26 @@ function upsertAgentAuthorityRun(projectRoot, input) {
168
179
  } else if ("errorText" in next) {
169
180
  delete next.errorText;
170
181
  }
171
- writeJsonFile(resolve2(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
182
+ writeAuthorityRunRecord(projectRoot, input.runId, next);
172
183
  return next;
173
184
  }
174
185
 
175
186
  // packages/cli/src/commands/_run-driver-helpers.ts
176
187
  function patchAuthorityRun(projectRoot, runId, patch) {
177
- const current = readAuthorityRun2(projectRoot, runId);
178
- if (!current) {
179
- throw new CliError2(`Run not found: ${runId}`, 2);
188
+ const next = patchAuthorityRunRecord(projectRoot, runId, patch);
189
+ if (!next) {
190
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
191
+ }
192
+ return next;
193
+ }
194
+ function setRunStatusOrFail(projectRoot, runId, to, options = {}) {
195
+ const next = setAuthorityRunStatus(projectRoot, runId, to, {
196
+ ...options,
197
+ actor: { kind: "agent" }
198
+ });
199
+ if (!next) {
200
+ throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
180
201
  }
181
- const next = {
182
- ...current,
183
- ...patch,
184
- updatedAt: new Date().toISOString()
185
- };
186
- writeJsonFile2(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), next);
187
202
  return next;
188
203
  }
189
204
  function touchAuthorityRun(projectRoot, runId) {
@@ -191,21 +206,21 @@ function touchAuthorityRun(projectRoot, runId) {
191
206
  if (!current) {
192
207
  return;
193
208
  }
194
- writeJsonFile2(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), {
209
+ writeJsonFile(resolve3(resolveAuthorityRunDir(projectRoot, runId), "run.json"), {
195
210
  ...current,
196
211
  updatedAt: new Date().toISOString()
197
212
  });
198
213
  }
199
214
  function appendRunTimeline(projectRoot, runId, value) {
200
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "timeline.jsonl"), value);
215
+ appendRunTimelineEntry(projectRoot, runId, value);
201
216
  touchAuthorityRun(projectRoot, runId);
202
217
  }
203
218
  function appendRunLog(projectRoot, runId, value) {
204
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "logs.jsonl"), value);
219
+ appendRunLogEntry(projectRoot, runId, value);
205
220
  touchAuthorityRun(projectRoot, runId);
206
221
  }
207
222
  function appendRunAction(projectRoot, runId, value) {
208
- appendJsonlRecord(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "timeline.jsonl"), {
223
+ appendRunTimelineEntry(projectRoot, runId, {
209
224
  id: value.id,
210
225
  type: "action",
211
226
  actionType: value.actionType,
@@ -259,8 +274,43 @@ function startRunAction(projectRoot, runId, input) {
259
274
  }
260
275
  };
261
276
  }
277
+ var runEventSinkProjectRoot = null;
278
+ function configureRunEventSink(projectRoot) {
279
+ runEventSinkProjectRoot = projectRoot;
280
+ }
281
+ var lastDoorbellRangAt = 0;
282
+ function ringServerRunDoorbell(runId) {
283
+ const serverUrl = process.env.RIG_SERVER_URL?.trim();
284
+ if (!serverUrl)
285
+ return;
286
+ const now = Date.now();
287
+ if (now - lastDoorbellRangAt < 250)
288
+ return;
289
+ lastDoorbellRangAt = now;
290
+ const token = process.env.RIG_AUTH_TOKEN || process.env.RIG_GITHUB_TOKEN || "";
291
+ fetch(`${serverUrl.replace(/\/+$/, "")}/api/runs/doorbell`, {
292
+ method: "POST",
293
+ headers: {
294
+ "content-type": "application/json",
295
+ authorization: `Bearer ${token}`
296
+ },
297
+ body: JSON.stringify({ runId }),
298
+ signal: AbortSignal.timeout(1000)
299
+ }).catch(() => {});
300
+ }
262
301
  function emitServerRunEvent(event) {
263
302
  console.log(`__RIG_RUN_EVENT__${JSON.stringify({ ...event, at: new Date().toISOString() })}`);
303
+ if (runEventSinkProjectRoot) {
304
+ try {
305
+ if (event.type === "status" || event.type === "completed" || event.type === "failed") {
306
+ appendRunJournalEvent(runEventSinkProjectRoot, event.runId, {
307
+ type: "timeline-entry",
308
+ payload: { kind: "driver-event", ...event }
309
+ });
310
+ }
311
+ } catch {}
312
+ ringServerRunDoorbell(event.runId);
313
+ }
264
314
  }
265
315
  function buildRunPrompt(input) {
266
316
  const initialPrompt = input.initialPrompt?.trim() || null;
@@ -317,6 +367,7 @@ ${acceptance}` : null,
317
367
  ${sourceContractLines.join(`
318
368
  `)}` : null,
319
369
  scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
370
+ readPriorPrProgressPromptSection(input.projectRoot, input.taskId),
320
371
  initialPrompt ? `Additional operator guidance:
321
372
  ${initialPrompt}` : null,
322
373
  providerLines.length > 0 ? `Provider-specific runtime tooling:
@@ -388,6 +439,322 @@ function renderSourceScopeValidation(task, validation) {
388
439
  `);
389
440
  }
390
441
 
442
+ // packages/cli/src/commands/_server-client.ts
443
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
444
+ import { resolve as resolve5 } from "path";
445
+ import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
446
+
447
+ // packages/cli/src/commands/_connection-state.ts
448
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
449
+ import { homedir } from "os";
450
+ import { dirname, resolve as resolve4 } from "path";
451
+ function resolveGlobalConnectionsPath(env = process.env) {
452
+ const explicit = env.RIG_CONNECTIONS_FILE?.trim();
453
+ if (explicit)
454
+ return resolve4(explicit);
455
+ const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
456
+ if (stateDir)
457
+ return resolve4(stateDir, "connections.json");
458
+ return resolve4(homedir(), ".rig", "connections.json");
459
+ }
460
+ function resolveRepoConnectionPath(projectRoot) {
461
+ return resolve4(projectRoot, ".rig", "state", "connection.json");
462
+ }
463
+ function readJsonFile(path) {
464
+ if (!existsSync2(path))
465
+ return null;
466
+ try {
467
+ return JSON.parse(readFileSync2(path, "utf8"));
468
+ } catch (error) {
469
+ throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
470
+ }
471
+ }
472
+ function writeJsonFile2(path, value) {
473
+ mkdirSync(dirname(path), { recursive: true });
474
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}
475
+ `, "utf8");
476
+ }
477
+ function normalizeConnection(value) {
478
+ if (!value || typeof value !== "object" || Array.isArray(value))
479
+ return null;
480
+ const record = value;
481
+ if (record.kind === "local")
482
+ return { kind: "local", mode: "auto" };
483
+ if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
484
+ const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
485
+ return { kind: "remote", baseUrl };
486
+ }
487
+ return null;
488
+ }
489
+ function readGlobalConnections(options = {}) {
490
+ const path = resolveGlobalConnectionsPath(options.env ?? process.env);
491
+ const payload = readJsonFile(path);
492
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
493
+ return { connections: {} };
494
+ }
495
+ const rawConnections = payload.connections;
496
+ const connections = {};
497
+ if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
498
+ for (const [alias, raw] of Object.entries(rawConnections)) {
499
+ const connection = normalizeConnection(raw);
500
+ if (connection)
501
+ connections[alias] = connection;
502
+ }
503
+ }
504
+ return { connections };
505
+ }
506
+ function readRepoConnection(projectRoot) {
507
+ const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
508
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
509
+ return null;
510
+ const record = payload;
511
+ const selected = typeof record.selected === "string" ? record.selected.trim() : "";
512
+ if (!selected)
513
+ return null;
514
+ return {
515
+ selected,
516
+ project: typeof record.project === "string" ? record.project : undefined,
517
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
518
+ serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
519
+ };
520
+ }
521
+ function writeRepoConnection(projectRoot, state) {
522
+ writeJsonFile2(resolveRepoConnectionPath(projectRoot), state);
523
+ }
524
+ function resolveSelectedConnection(projectRoot, options = {}) {
525
+ const repo = readRepoConnection(projectRoot);
526
+ if (!repo)
527
+ return null;
528
+ if (repo.selected === "local")
529
+ return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
530
+ const global = readGlobalConnections(options);
531
+ const connection = global.connections[repo.selected];
532
+ if (!connection) {
533
+ throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
534
+ }
535
+ return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
536
+ }
537
+ function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
538
+ const repo = readRepoConnection(projectRoot);
539
+ if (!repo)
540
+ return;
541
+ writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
542
+ }
543
+
544
+ // packages/cli/src/commands/_server-client.ts
545
+ var scopedGitHubBearerTokens = new Map;
546
+ var serverPhaseListener = null;
547
+ function reportServerPhase(label) {
548
+ serverPhaseListener?.(label);
549
+ }
550
+ function cleanToken(value) {
551
+ const trimmed = value?.trim();
552
+ return trimmed ? trimmed : null;
553
+ }
554
+ function readPrivateRemoteSessionToken(projectRoot) {
555
+ const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
556
+ if (!existsSync3(path))
557
+ return null;
558
+ try {
559
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
560
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
561
+ } catch {
562
+ return null;
563
+ }
564
+ }
565
+ function readGitHubBearerTokenForRemote(projectRoot) {
566
+ const scopedKey = resolve5(projectRoot);
567
+ if (scopedGitHubBearerTokens.has(scopedKey))
568
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
569
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
570
+ if (privateSession)
571
+ return privateSession;
572
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
573
+ }
574
+ function readStoredGitHubAuthToken(projectRoot) {
575
+ const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
576
+ if (!existsSync3(path))
577
+ return null;
578
+ try {
579
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
580
+ return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
581
+ } catch {
582
+ return null;
583
+ }
584
+ }
585
+ function readLocalConnectionFallbackToken(projectRoot) {
586
+ return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
587
+ }
588
+ async function ensureServerForCli(projectRoot) {
589
+ try {
590
+ const selected = resolveSelectedConnection(projectRoot);
591
+ if (selected?.connection.kind === "remote") {
592
+ reportServerPhase(`Connecting to ${selected.alias}\u2026`);
593
+ const authToken = readGitHubBearerTokenForRemote(projectRoot);
594
+ const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
595
+ return {
596
+ baseUrl: selected.connection.baseUrl,
597
+ authToken,
598
+ connectionKind: "remote",
599
+ serverProjectRoot
600
+ };
601
+ }
602
+ reportServerPhase("Starting local Rig server\u2026");
603
+ const connection = await ensureLocalRigServerConnection(projectRoot);
604
+ return {
605
+ baseUrl: connection.baseUrl,
606
+ authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
607
+ connectionKind: "local",
608
+ serverProjectRoot: resolve5(projectRoot)
609
+ };
610
+ } catch (error) {
611
+ if (error instanceof Error) {
612
+ throw new CliError(error.message, 1);
613
+ }
614
+ throw error;
615
+ }
616
+ }
617
+ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
618
+ const repo = readRepoConnection(projectRoot);
619
+ const slug = repo?.project?.trim();
620
+ if (!slug)
621
+ return null;
622
+ try {
623
+ const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
624
+ headers: mergeHeaders(undefined, authToken)
625
+ });
626
+ if (!response.ok)
627
+ return null;
628
+ const payload = await response.json();
629
+ const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
630
+ const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
631
+ const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
632
+ const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
633
+ if (path)
634
+ writeRepoServerProjectRoot(projectRoot, path);
635
+ return path;
636
+ } catch {
637
+ return null;
638
+ }
639
+ }
640
+ function mergeHeaders(headers, authToken) {
641
+ const merged = new Headers(headers);
642
+ if (authToken) {
643
+ merged.set("authorization", `Bearer ${authToken}`);
644
+ }
645
+ return merged;
646
+ }
647
+ function diagnosticMessage(payload) {
648
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
649
+ return null;
650
+ const record = payload;
651
+ const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
652
+ const messages = diagnostics.flatMap((entry) => {
653
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
654
+ return [];
655
+ const diagnostic = entry;
656
+ const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
657
+ const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
658
+ return message ? [`${kind}: ${message}`] : [];
659
+ });
660
+ return messages.length > 0 ? messages.join("; ") : null;
661
+ }
662
+ var serverReachabilityCache = new Map;
663
+ async function probeServerReachability(baseUrl, authToken) {
664
+ try {
665
+ const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
666
+ headers: mergeHeaders(undefined, authToken),
667
+ signal: AbortSignal.timeout(1500)
668
+ });
669
+ return response.ok;
670
+ } catch {
671
+ return false;
672
+ }
673
+ }
674
+ function cachedServerReachability(projectRoot, baseUrl, authToken) {
675
+ const key = resolve5(projectRoot);
676
+ const cached = serverReachabilityCache.get(key);
677
+ if (cached)
678
+ return cached;
679
+ const probe = probeServerReachability(baseUrl, authToken);
680
+ serverReachabilityCache.set(key, probe);
681
+ return probe;
682
+ }
683
+ function describeSelectedServer(projectRoot, server) {
684
+ try {
685
+ const selected = resolveSelectedConnection(projectRoot);
686
+ if (selected) {
687
+ return {
688
+ alias: selected.alias,
689
+ target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
690
+ };
691
+ }
692
+ } catch {}
693
+ return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
694
+ }
695
+ async function buildServerFailureContext(projectRoot, server) {
696
+ const { alias, target } = describeSelectedServer(projectRoot, server);
697
+ const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
698
+ const reachability = reachable ? "server is reachable" : "server is unreachable";
699
+ return {
700
+ contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
701
+ hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
702
+ };
703
+ }
704
+ async function requestServerJson(context, pathname, init = {}) {
705
+ const server = await ensureServerForCli(context.projectRoot);
706
+ const headers = mergeHeaders(init.headers, server.authToken);
707
+ if (server.serverProjectRoot)
708
+ headers.set("x-rig-project-root", server.serverProjectRoot);
709
+ reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
710
+ let response;
711
+ try {
712
+ response = await fetch(`${server.baseUrl}${pathname}`, {
713
+ ...init,
714
+ headers
715
+ });
716
+ } catch (error) {
717
+ const failure = await buildServerFailureContext(context.projectRoot, server);
718
+ throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
719
+ ${failure.contextLine}`, 1, { hint: failure.hint });
720
+ }
721
+ const text = await response.text();
722
+ const payload = text.trim().length > 0 ? (() => {
723
+ try {
724
+ return JSON.parse(text);
725
+ } catch {
726
+ return null;
727
+ }
728
+ })() : null;
729
+ if (!response.ok) {
730
+ const diagnostics = diagnosticMessage(payload);
731
+ const detail = diagnostics ?? (text || response.statusText);
732
+ const failure = await buildServerFailureContext(context.projectRoot, server);
733
+ throw new CliError(`Rig server request failed (${response.status}): ${detail}
734
+ ${failure.contextLine}`, 1, { hint: failure.hint });
735
+ }
736
+ return payload;
737
+ }
738
+ async function updateWorkspaceTaskViaServer(context, input) {
739
+ const payload = await requestServerJson(context, "/api/tasks/update", {
740
+ method: "POST",
741
+ headers: { "content-type": "application/json" },
742
+ body: JSON.stringify(input)
743
+ });
744
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
745
+ }
746
+ var RESUMABLE_RUN_STATUSES = new Set([
747
+ "created",
748
+ "preparing",
749
+ "running",
750
+ "validating",
751
+ "reviewing",
752
+ "stopped",
753
+ "failed",
754
+ "needs-attention",
755
+ "needs_attention"
756
+ ]);
757
+
391
758
  // packages/cli/src/commands/task-run-driver.ts
392
759
  var PI_CANONICAL_RUN_STAGES = [
393
760
  "Connect",
@@ -431,6 +798,7 @@ function buildPiRigBridgeEnv(input) {
431
798
  RIG_SERVER_RUN_ID: input.runId,
432
799
  RIG_TASK_ID: input.taskId,
433
800
  RIG_RUNTIME_ADAPTER: "pi",
801
+ RIG_STEERING_POLL_MS: "0",
434
802
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
435
803
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
436
804
  ...githubBridgeEnv(githubToken)
@@ -455,12 +823,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
455
823
  return 0;
456
824
  let copied = 0;
457
825
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
458
- const sourcePath = resolve4(sourceRoot, relativePath);
459
- const targetPath = resolve4(targetRoot, relativePath);
826
+ const sourcePath = resolve6(sourceRoot, relativePath);
827
+ const targetPath = resolve6(targetRoot, relativePath);
460
828
  try {
461
829
  if (!statSync(sourcePath).isFile())
462
830
  continue;
463
- mkdirSync(resolve4(targetPath, ".."), { recursive: true });
831
+ mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
464
832
  copyFileSync(sourcePath, targetPath);
465
833
  copied += 1;
466
834
  } catch {}
@@ -499,7 +867,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
499
867
  return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
500
868
  return {
501
869
  RIG_BASELINE_MODE: "dirty-snapshot",
502
- RIG_DIRTY_BASELINE_READY_FILE: resolve4(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
870
+ RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
503
871
  };
504
872
  }
505
873
  function positiveInt(value, fallback) {
@@ -507,7 +875,7 @@ function positiveInt(value, fallback) {
507
875
  }
508
876
  function resolveTaskRunAutomationLimits(config, env = process.env) {
509
877
  const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
510
- const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 30);
878
+ const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
511
879
  return {
512
880
  maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
513
881
  maxPrFixIterations: configuredPrFixIterations
@@ -601,12 +969,6 @@ function appendPiStageLog(input) {
601
969
  });
602
970
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
603
971
  }
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
972
  function createCommandRunner(binary) {
611
973
  return async (args, options) => {
612
974
  const child = spawn(binary, [...args], {
@@ -617,9 +979,9 @@ function createCommandRunner(binary) {
617
979
  const stderrChunks = [];
618
980
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
619
981
  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({
982
+ return await new Promise((resolve7) => {
983
+ child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
984
+ child.once("close", (code) => resolve7({
623
985
  exitCode: code ?? 1,
624
986
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
625
987
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -643,8 +1005,9 @@ async function runTaskRunPostValidationLifecycle(input) {
643
1005
  if (!taskId) {
644
1006
  return { status: "skipped" };
645
1007
  }
646
- const config = input.config ?? {};
647
- const prMode = config.pr?.mode ?? "auto";
1008
+ const configInput = input.config ?? null;
1009
+ const config = configInput ?? {};
1010
+ const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
648
1011
  if (prMode === "off" || prMode === "ask") {
649
1012
  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
1013
  input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
@@ -660,7 +1023,7 @@ async function runTaskRunPostValidationLifecycle(input) {
660
1023
  gitCommand
661
1024
  });
662
1025
  const prAutomation = input.prAutomation ?? runPrAutomation;
663
- const updateTaskSource = input.updateTaskSource ?? updateConfiguredTaskSourceTask;
1026
+ const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
664
1027
  const stage = input.appendStage ?? (() => {
665
1028
  return;
666
1029
  });
@@ -682,7 +1045,7 @@ async function runTaskRunPostValidationLifecycle(input) {
682
1045
  command: gitCommand
683
1046
  });
684
1047
  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");
1048
+ await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
686
1049
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
687
1050
  const pr = await prAutomation({
688
1051
  projectRoot: workspace,
@@ -692,7 +1055,9 @@ async function runTaskRunPostValidationLifecycle(input) {
692
1055
  config,
693
1056
  sourceTask: input.sourceTask,
694
1057
  uploadedSnapshot: input.uploadedSnapshot,
1058
+ artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
695
1059
  command: ghCommand,
1060
+ gitCommand,
696
1061
  steerPi,
697
1062
  lifecycle: {
698
1063
  onPrOpened: async ({ prUrl }) => {
@@ -741,8 +1106,6 @@ async function runTaskRunPostValidationLifecycle(input) {
741
1106
  runtimeWorkspace: input.runtimeWorkspace ?? null
742
1107
  })
743
1108
  }
744
- }).catch(() => {
745
- return;
746
1109
  });
747
1110
  }
748
1111
  },
@@ -761,8 +1124,6 @@ async function runTaskRunPostValidationLifecycle(input) {
761
1124
  runtimeWorkspace: input.runtimeWorkspace ?? null
762
1125
  })
763
1126
  }
764
- }).catch(() => {
765
- return;
766
1127
  });
767
1128
  }
768
1129
  },
@@ -791,8 +1152,17 @@ async function runTaskRunPostValidationLifecycle(input) {
791
1152
  errorText: detail
792
1153
  })
793
1154
  }
794
- }).catch(() => {
795
- return;
1155
+ }).catch((error) => {
1156
+ try {
1157
+ appendRunLog(input.projectRoot, input.runId, {
1158
+ id: `log:${input.runId}:task-source-needs-attention-update`,
1159
+ title: "Task source needs-attention update failed",
1160
+ detail: error instanceof Error ? error.message : String(error),
1161
+ tone: "error",
1162
+ status: "needs_attention",
1163
+ createdAt: new Date().toISOString()
1164
+ });
1165
+ } catch {}
796
1166
  });
797
1167
  }
798
1168
  return { status: "needs_attention", pr };
@@ -815,7 +1185,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
815
1185
  return null;
816
1186
  }
817
1187
  for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
818
- const summary = readJsonFile(resolve4(artifactDir, "validation-summary.json"), null);
1188
+ const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
819
1189
  if (!summary || summary.status !== "fail") {
820
1190
  continue;
821
1191
  }
@@ -888,7 +1258,7 @@ function parsePositiveInt(value, option, fallback) {
888
1258
  }
889
1259
  const parsed = Number.parseInt(value, 10);
890
1260
  if (!Number.isFinite(parsed) || parsed <= 0) {
891
- throw new CliError2(`Invalid ${option} value: ${value}`);
1261
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
892
1262
  }
893
1263
  return parsed;
894
1264
  }
@@ -896,14 +1266,14 @@ function readTaskRunAcceptedArtifactState(input) {
896
1266
  if (!input.taskId || !input.workspaceDir) {
897
1267
  return { accepted: false, reason: null };
898
1268
  }
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");
1269
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1270
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1271
+ const taskResultPath = resolve6(artifactDir, "task-result.json");
902
1272
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
903
1273
  if (reviewStatus !== "APPROVED") {
904
1274
  return { accepted: false, reason: null };
905
1275
  }
906
- const taskResult = readJsonFile(taskResultPath, null);
1276
+ const taskResult = readJsonFile2(taskResultPath, null);
907
1277
  if (taskResult?.status !== "completed") {
908
1278
  return { accepted: false, reason: null };
909
1279
  }
@@ -935,13 +1305,13 @@ function resolveTaskRunRetryContext(input) {
935
1305
  if (!input.taskId || !input.workspaceDir) {
936
1306
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
937
1307
  }
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);
1308
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1309
+ const reviewStatePath = resolve6(artifactDir, "review-state.json");
1310
+ const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
1311
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1312
+ const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
1313
+ const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
1314
+ const reviewState = readJsonFile2(reviewStatePath, null);
945
1315
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
946
1316
  const reviewRejected = isTaskRunReviewRejected(reviewState);
947
1317
  if (reviewStatus === "APPROVED") {
@@ -994,12 +1364,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
994
1364
  }
995
1365
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
996
1366
  }
1367
+ function appendAssistantTimelineFromRecord(input) {
1368
+ let nextAssistantText = input.assistantText;
1369
+ if (input.record.type === "message_update") {
1370
+ const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
1371
+ if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
1372
+ nextAssistantText += assistantMessageEvent.delta;
1373
+ }
1374
+ } else if (input.record.type === "stream_event") {
1375
+ const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
1376
+ const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
1377
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
1378
+ nextAssistantText += delta.text;
1379
+ }
1380
+ } else if (input.record.type === "assistant") {
1381
+ const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
1382
+ const content = Array.isArray(message.content) ? message.content : [];
1383
+ const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
1384
+ if (fullText.length > nextAssistantText.length)
1385
+ nextAssistantText = fullText;
1386
+ }
1387
+ if (nextAssistantText !== input.assistantText) {
1388
+ appendRunTimeline(input.projectRoot, input.runId, {
1389
+ id: input.messageId,
1390
+ type: "assistant_message",
1391
+ text: nextAssistantText,
1392
+ state: "streaming",
1393
+ createdAt: new Date().toISOString()
1394
+ });
1395
+ }
1396
+ return nextAssistantText;
1397
+ }
1398
+ function appendPiRpcProtocolLogFromRecord(input) {
1399
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1400
+ if (type === "response") {
1401
+ const command = typeof input.record.command === "string" ? input.record.command : "rpc";
1402
+ const success = input.record.success !== false;
1403
+ if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
1404
+ return true;
1405
+ }
1406
+ appendRunLog(input.projectRoot, input.runId, {
1407
+ id: input.nextRunLogId(),
1408
+ title: success ? "Pi RPC response" : "Pi RPC error",
1409
+ detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
1410
+ tone: success ? "tool" : "error",
1411
+ status: input.status,
1412
+ payload: input.record,
1413
+ createdAt: new Date().toISOString()
1414
+ });
1415
+ emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
1416
+ return true;
1417
+ }
1418
+ if (type !== "extension_ui_request")
1419
+ return false;
1420
+ const method = typeof input.record.method === "string" ? input.record.method : "ui";
1421
+ let title = "Pi UI event";
1422
+ let detail = method;
1423
+ let tone = "info";
1424
+ if (method === "notify") {
1425
+ title = "Pi notification";
1426
+ detail = String(input.record.message ?? "");
1427
+ tone = input.record.notifyType === "error" ? "error" : "info";
1428
+ } else if (method === "setStatus") {
1429
+ title = "Pi UI status";
1430
+ detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
1431
+ tone = "tool";
1432
+ } else if (method === "setWidget") {
1433
+ title = "Pi UI widget";
1434
+ const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
1435
+ detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
1436
+ tone = "tool";
1437
+ } else if (method === "setTitle") {
1438
+ title = "Pi UI title";
1439
+ detail = String(input.record.title ?? "");
1440
+ tone = "tool";
1441
+ } else if (method === "set_editor_text") {
1442
+ title = "Pi editor update";
1443
+ detail = String(input.record.text ?? "").slice(0, 500);
1444
+ tone = "tool";
1445
+ } else {
1446
+ title = "Pi UI request";
1447
+ detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
1448
+ }
1449
+ appendRunLog(input.projectRoot, input.runId, {
1450
+ id: input.nextRunLogId(),
1451
+ title,
1452
+ detail,
1453
+ tone,
1454
+ status: input.status,
1455
+ payload: input.record,
1456
+ createdAt: new Date().toISOString()
1457
+ });
1458
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1459
+ return true;
1460
+ }
1461
+ function appendPiToolTimelineFromRecord(input) {
1462
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1463
+ if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
1464
+ return false;
1465
+ const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
1466
+ const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
1467
+ const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
1468
+ appendRunTimeline(input.projectRoot, input.runId, {
1469
+ id: `tool:${toolCallId}:${type}`,
1470
+ type,
1471
+ toolName,
1472
+ status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
1473
+ createdAt: new Date().toISOString()
1474
+ });
1475
+ return true;
1476
+ }
1477
+ function isNonRenderablePiProtocolRecord(record) {
1478
+ const type = typeof record.type === "string" ? record.type : "";
1479
+ 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");
1480
+ }
1481
+ function appendToolTimelineFromLog(input) {
1482
+ const title = typeof input.log.title === "string" ? input.log.title : "";
1483
+ if (title !== "Tool activity")
1484
+ return;
1485
+ const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
1486
+ 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;
1487
+ const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
1488
+ appendRunTimeline(input.projectRoot, input.runId, {
1489
+ id: `tool:${logId}`,
1490
+ type: "tool_execution_update",
1491
+ toolName,
1492
+ status: typeof input.log.status === "string" ? input.log.status : "running",
1493
+ detail: typeof input.log.detail === "string" ? input.log.detail : null,
1494
+ payload,
1495
+ createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
1496
+ });
1497
+ }
997
1498
  function readTaskRunReviewStatus(reviewStatusPath) {
998
- if (!existsSync2(reviewStatusPath)) {
1499
+ if (!existsSync4(reviewStatusPath)) {
999
1500
  return null;
1000
1501
  }
1001
1502
  try {
1002
- const status = readFileSync2(reviewStatusPath, "utf8").trim().toUpperCase();
1503
+ const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
1003
1504
  return status === "APPROVED" || status === "REJECTED" ? status : null;
1004
1505
  } catch {
1005
1506
  return null;
@@ -1017,7 +1518,38 @@ function isTaskRunReviewRejected(reviewState) {
1017
1518
  function runSourceTaskIdentity(sourceTask) {
1018
1519
  return sourceTask;
1019
1520
  }
1020
- async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateConfiguredTaskSourceTask) {
1521
+ function sourceTaskIssueNodeId(sourceTask) {
1522
+ if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
1523
+ return null;
1524
+ const record = sourceTask;
1525
+ const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
1526
+ if (direct?.trim())
1527
+ return direct.trim();
1528
+ const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
1529
+ return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
1530
+ }
1531
+ var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
1532
+ const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
1533
+ id: input.taskId,
1534
+ ...input.update.status ? { status: input.update.status } : {},
1535
+ ...input.update.comment ? { comment: input.update.comment } : {},
1536
+ ...input.update.title ? { title: input.update.title } : {},
1537
+ ...typeof input.update.body === "string" ? { body: input.update.body } : {},
1538
+ issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
1539
+ });
1540
+ if (serverResult.ok === false) {
1541
+ throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
1542
+ }
1543
+ return {
1544
+ updated: serverResult.ok !== false,
1545
+ taskId: input.taskId,
1546
+ status: input.update.status,
1547
+ source: "server",
1548
+ sourceKind: "server",
1549
+ projectSync: serverResult.projectSync
1550
+ };
1551
+ };
1552
+ async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
1021
1553
  if (!taskId)
1022
1554
  return;
1023
1555
  const config = await loadTaskRunAutomationConfig(projectRoot);
@@ -1081,9 +1613,14 @@ function stringArrayField(record, key) {
1081
1613
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
1082
1614
  }
1083
1615
  async function executeRigOwnedTaskRun(context, input) {
1616
+ process.stdout.on("error", () => {});
1617
+ process.stderr.on("error", () => {});
1618
+ configureRunEventSink(context.projectRoot);
1084
1619
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
1085
1620
  const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
1086
1621
  const resumeMode = process.env.RIG_RUN_RESUME === "1";
1622
+ let latestPiSessionId = typeof existingRunRecord?.piSession?.sessionId === "string" && existingRunRecord.piSession.sessionId.trim() ? existingRunRecord.piSession.sessionId : null;
1623
+ const sessionAnchor = () => latestPiSessionId ? { sessionId: latestPiSessionId } : {};
1087
1624
  const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
1088
1625
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
1089
1626
  let prompt = buildRunPrompt({
@@ -1107,11 +1644,7 @@ async function executeRigOwnedTaskRun(context, input) {
1107
1644
  ...input.model ? ["--model", input.model] : [],
1108
1645
  "--prompt"
1109
1646
  ] : input.runtimeAdapter === "pi" ? [
1110
- "--print",
1111
- "--verbose",
1112
- "--mode",
1113
- "json",
1114
- "--no-session",
1647
+ "__rig_pi_session_daemon__",
1115
1648
  ...input.model ? ["--model", input.model] : []
1116
1649
  ] : [
1117
1650
  "--print",
@@ -1142,8 +1675,8 @@ async function executeRigOwnedTaskRun(context, input) {
1142
1675
  runtimeAdapter: input.runtimeAdapter,
1143
1676
  status: resumeMode ? "preparing" : "created"
1144
1677
  });
1678
+ setRunStatusOrFail(context.projectRoot, input.runId, "preparing", { reason: "driver-start" });
1145
1679
  patchAuthorityRun(context.projectRoot, input.runId, {
1146
- status: "preparing",
1147
1680
  startedAt,
1148
1681
  completedAt: null,
1149
1682
  errorText: null,
@@ -1160,7 +1693,8 @@ async function executeRigOwnedTaskRun(context, input) {
1160
1693
  type: "status",
1161
1694
  runId: input.runId,
1162
1695
  status: "preparing",
1163
- detail: input.taskId ?? input.title ?? runtimeTaskId
1696
+ detail: input.taskId ?? input.title ?? runtimeTaskId,
1697
+ ...sessionAnchor()
1164
1698
  });
1165
1699
  appendRunLog(context.projectRoot, input.runId, {
1166
1700
  id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
@@ -1186,15 +1720,15 @@ async function executeRigOwnedTaskRun(context, input) {
1186
1720
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
1187
1721
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
1188
1722
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
1189
- const planningArtifactPath = resolve4("artifacts", runtimeTaskId, "implementation-plan.md");
1723
+ const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
1190
1724
  const persistedPlanning = {
1191
1725
  ...planningClassification,
1192
1726
  classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
1193
1727
  artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
1194
1728
  classifiedAt: new Date().toISOString()
1195
1729
  };
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)}
1730
+ mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
1731
+ writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
1198
1732
  `, "utf8");
1199
1733
  patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
1200
1734
  prompt = `${prompt}
@@ -1208,7 +1742,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1208
1742
  projectRoot: context.projectRoot,
1209
1743
  runId: input.runId,
1210
1744
  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,
1745
+ 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
1746
  status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
1213
1747
  });
1214
1748
  }
@@ -1244,11 +1778,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1244
1778
  let verificationStarted = false;
1245
1779
  let reviewStarted = false;
1246
1780
  let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
1247
- let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve4(existingRunRecord.sessionPath, "..") : null;
1781
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
1248
1782
  let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
1249
1783
  let latestProviderCommand = null;
1250
1784
  let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
1251
1785
  let snapshotSidecarPromise = null;
1786
+ let wrapperManagesRuntimeSnapshot = false;
1252
1787
  let dirtyBaselineApplied = false;
1253
1788
  const childEnv = {
1254
1789
  ...process.env,
@@ -1279,8 +1814,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1279
1814
  if (verificationStarted)
1280
1815
  return;
1281
1816
  verificationStarted = true;
1282
- patchAuthorityRun(context.projectRoot, input.runId, { status: "validating" });
1283
- emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null });
1817
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating");
1818
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null, ...sessionAnchor() });
1284
1819
  verificationAction = startRunAction(context.projectRoot, input.runId, {
1285
1820
  actionId: `action:${input.runId}:completion-verification`,
1286
1821
  actionType: "completion-verification",
@@ -1292,8 +1827,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1292
1827
  if (reviewStarted)
1293
1828
  return;
1294
1829
  reviewStarted = true;
1295
- patchAuthorityRun(context.projectRoot, input.runId, { status: "reviewing" });
1296
- emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null });
1830
+ setRunStatusOrFail(context.projectRoot, input.runId, "reviewing");
1831
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null, ...sessionAnchor() });
1297
1832
  reviewAction = startRunAction(context.projectRoot, input.runId, {
1298
1833
  actionId: `action:${input.runId}:review-closeout`,
1299
1834
  actionType: "run.review",
@@ -1301,6 +1836,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1301
1836
  detail: detail ?? "Verifier review is running."
1302
1837
  });
1303
1838
  };
1839
+ const nextRunLogId = createRunLogIdFactory(input.runId);
1304
1840
  const handleWrapperEvent = (rawPayload) => {
1305
1841
  let event = null;
1306
1842
  try {
@@ -1316,6 +1852,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1316
1852
  latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
1317
1853
  latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
1318
1854
  const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
1855
+ wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
1319
1856
  latestRuntimeBranch = runtimeId;
1320
1857
  provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
1321
1858
  runtimeId: runtimeId ?? null,
@@ -1324,13 +1861,13 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1324
1861
  logsDir: latestLogsDir,
1325
1862
  contextFile: payload.contextFile ?? null
1326
1863
  });
1864
+ setRunStatusOrFail(context.projectRoot, input.runId, "running");
1327
1865
  patchAuthorityRun(context.projectRoot, input.runId, {
1328
- status: "running",
1329
1866
  worktreePath: latestRuntimeWorkspace,
1330
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve4(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1867
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1331
1868
  logRoot: latestLogsDir,
1332
- sessionPath: latestSessionDir ? resolve4(latestSessionDir, "session.json") : null,
1333
- sessionLogPath: latestLogsDir ? resolve4(latestLogsDir, "agent-stdout.log") : null,
1869
+ sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
1870
+ sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
1334
1871
  branch: runtimeId
1335
1872
  });
1336
1873
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
@@ -1338,8 +1875,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1338
1875
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
1339
1876
  const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
1340
1877
  if (readyFile) {
1341
- mkdirSync(resolve4(readyFile, ".."), { recursive: true });
1342
- writeFileSync(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1878
+ mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
1879
+ writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1343
1880
  `, "utf8");
1344
1881
  }
1345
1882
  appendRunLog(context.projectRoot, input.runId, {
@@ -1353,7 +1890,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1353
1890
  });
1354
1891
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
1355
1892
  }
1356
- if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1893
+ if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1357
1894
  snapshotSidecarPromise = (async () => {
1358
1895
  const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
1359
1896
  projectRoot: context.projectRoot,
@@ -1381,7 +1918,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1381
1918
  type: "status",
1382
1919
  runId: input.runId,
1383
1920
  status: "running",
1384
- detail: latestRuntimeWorkspace
1921
+ detail: latestRuntimeWorkspace,
1922
+ ...sessionAnchor()
1385
1923
  });
1386
1924
  return true;
1387
1925
  }
@@ -1435,9 +1973,71 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1435
1973
  }
1436
1974
  return true;
1437
1975
  }
1976
+ 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") {
1977
+ 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";
1978
+ 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");
1979
+ appendRunLog(context.projectRoot, input.runId, {
1980
+ id: nextRunLogId(),
1981
+ title,
1982
+ detail,
1983
+ tone: event.type === "pi.session.error" ? "error" : "info",
1984
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
1985
+ payload: {
1986
+ eventType: event.type,
1987
+ runId: typeof payload.runId === "string" ? payload.runId : null,
1988
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
1989
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
1990
+ },
1991
+ createdAt: new Date().toISOString()
1992
+ });
1993
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1994
+ return true;
1995
+ }
1996
+ if (event.type === "pi.session.ready") {
1997
+ const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
1998
+ const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
1999
+ if (privateMetadata) {
2000
+ patchAuthorityRun(context.projectRoot, input.runId, {
2001
+ piSession: publicMetadata,
2002
+ piSessionPrivate: privateMetadata
2003
+ });
2004
+ const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
2005
+ if (sessionId) {
2006
+ latestPiSessionId = sessionId;
2007
+ }
2008
+ appendRunLog(context.projectRoot, input.runId, {
2009
+ id: `log:${input.runId}:pi-session-ready`,
2010
+ title: "Worker Pi session ready",
2011
+ detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
2012
+ tone: "info",
2013
+ status: "running",
2014
+ payload: {
2015
+ sessionId: sessionId || null,
2016
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
2017
+ },
2018
+ createdAt: new Date().toISOString()
2019
+ });
2020
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
2021
+ }
2022
+ return true;
2023
+ }
2024
+ 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") {
2025
+ 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";
2026
+ 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")}`;
2027
+ appendRunLog(context.projectRoot, input.runId, {
2028
+ id: nextRunLogId(),
2029
+ title,
2030
+ detail,
2031
+ tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
2032
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
2033
+ payload,
2034
+ createdAt: new Date().toISOString()
2035
+ });
2036
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
2037
+ return true;
2038
+ }
1438
2039
  return false;
1439
2040
  };
1440
- const nextRunLogId = createRunLogIdFactory(input.runId);
1441
2041
  const handleAgentStdoutLine = (line) => {
1442
2042
  const trimmed = line.trim();
1443
2043
  if (!trimmed)
@@ -1471,6 +2071,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1471
2071
  try {
1472
2072
  const record = JSON.parse(trimmed);
1473
2073
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
2074
+ if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
2075
+ projectRoot: context.projectRoot,
2076
+ runId: input.runId,
2077
+ record,
2078
+ status: liveLogStatus,
2079
+ nextRunLogId
2080
+ })) {
2081
+ return;
2082
+ }
2083
+ if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
2084
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2085
+ return;
2086
+ }
1474
2087
  const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
1475
2088
  runId: input.runId,
1476
2089
  record,
@@ -1487,7 +2100,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1487
2100
  if (providerLogs.length > 0) {
1488
2101
  for (const providerLog of providerLogs) {
1489
2102
  appendRunLog(context.projectRoot, input.runId, providerLog);
2103
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
1490
2104
  emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2105
+ if (providerLog.title === "Tool activity")
2106
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1491
2107
  }
1492
2108
  }
1493
2109
  if (input.runtimeAdapter === "codex") {
@@ -1539,6 +2155,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1539
2155
  return;
1540
2156
  }
1541
2157
  }
2158
+ if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
2159
+ return;
2160
+ }
1542
2161
  if (record.type === "assistant") {
1543
2162
  const message = record.message && typeof record.message === "object" ? record.message : record;
1544
2163
  const content = Array.isArray(message.content) ? message.content : [];
@@ -1645,7 +2264,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1645
2264
  let acceptedArtifactObservedAt = null;
1646
2265
  let acceptedArtifactPollTimer = null;
1647
2266
  let acceptedArtifactKillTimer = null;
1648
- const attemptExit = await new Promise((resolve5) => {
2267
+ const attemptExit = await new Promise((resolve7) => {
1649
2268
  let settled = false;
1650
2269
  const settle = (result) => {
1651
2270
  if (settled)
@@ -1653,7 +2272,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1653
2272
  settled = true;
1654
2273
  if (acceptedArtifactPollTimer)
1655
2274
  clearInterval(acceptedArtifactPollTimer);
1656
- resolve5(result);
2275
+ resolve7(result);
1657
2276
  };
1658
2277
  const pollAcceptedArtifacts = () => {
1659
2278
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -1726,11 +2345,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1726
2345
  });
1727
2346
  for (const pendingLog of pendingLogs) {
1728
2347
  appendRunLog(context.projectRoot, input.runId, pendingLog);
2348
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
1729
2349
  emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2350
+ if (pendingLog.title === "Tool activity")
2351
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1730
2352
  }
1731
2353
  process.off("SIGTERM", forwardSigterm);
1732
2354
  if (attemptExit.error) {
1733
- throw new CliError2(`Task run failed to start: ${attemptExit.error.message}`, 1);
2355
+ throw new CliError(`Task run failed to start: ${attemptExit.error.message}`, 1);
1734
2356
  }
1735
2357
  const retryContext = resolveTaskRunRetryContext({
1736
2358
  projectRoot: context.projectRoot,
@@ -1752,8 +2374,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1752
2374
  currentPrompt = [retryContext.nextPrompt, "", "Rig Pi steering message:", retrySteering].join(`
1753
2375
  `);
1754
2376
  reviewFailureDetail = failureDetail;
2377
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating", { reason: "validation-retry" });
1755
2378
  patchAuthorityRun(context.projectRoot, input.runId, {
1756
- status: "validating",
1757
2379
  completedAt: null,
1758
2380
  errorText: null
1759
2381
  });
@@ -1777,7 +2399,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1777
2399
  type: "status",
1778
2400
  runId: input.runId,
1779
2401
  status: "validating",
1780
- detail: failureDetail
2402
+ detail: failureDetail,
2403
+ ...sessionAnchor()
1781
2404
  });
1782
2405
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Validate failed" });
1783
2406
  emitServerRunEvent({ type: "log", runId: input.runId, title: `Steering Pi to retry validation (attempt ${attempt + 1}/${maxAttempts})` });
@@ -1788,10 +2411,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1788
2411
  break;
1789
2412
  }
1790
2413
  if (!exit) {
1791
- throw new CliError2(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
2414
+ throw new CliError(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
1792
2415
  }
1793
2416
  if (exit.error) {
1794
- throw new CliError2(`Task run failed to start: ${exit.error.message}`, 1);
2417
+ throw new CliError(`Task run failed to start: ${exit.error.message}`, 1);
1795
2418
  }
1796
2419
  if (assistantText.trim()) {
1797
2420
  appendRunTimeline(context.projectRoot, input.runId, {
@@ -1818,9 +2441,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1818
2441
  reviewAction.fail(failureDetail);
1819
2442
  }
1820
2443
  let terminalFailureDetail = failureDetail;
1821
- const terminalRunStatus = reviewFailureDetail ? "needs_attention" : "failed";
2444
+ const terminalRunStatus = reviewFailureDetail ? "needs-attention" : "failed";
1822
2445
  try {
1823
- await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs_attention" ? "Rig task run needs attention." : "Rig task run failed.", {
2446
+ await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs-attention" ? "Rig task run needs attention." : "Rig task run failed.", {
1824
2447
  latestRuntimeWorkspace,
1825
2448
  latestLogsDir,
1826
2449
  latestSessionDir,
@@ -1830,14 +2453,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1830
2453
  terminalFailureDetail = `${failureDetail}
1831
2454
  Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${error instanceof Error ? error.message : String(error)}`;
1832
2455
  }
1833
- patchAuthorityRun(context.projectRoot, input.runId, {
1834
- status: terminalRunStatus,
1835
- completedAt: failedAt,
2456
+ setRunStatusOrFail(context.projectRoot, input.runId, terminalRunStatus, {
2457
+ reason: "driver-terminal-failure",
1836
2458
  errorText: terminalFailureDetail
1837
2459
  });
2460
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1838
2461
  appendRunLog(context.projectRoot, input.runId, {
1839
2462
  id: `log:${input.runId}:${terminalRunStatus}`,
1840
- title: terminalRunStatus === "needs_attention" ? "Task run needs attention" : "Task run failed",
2463
+ title: terminalRunStatus === "needs-attention" ? "Task run needs attention" : "Task run failed",
1841
2464
  detail: terminalFailureDetail,
1842
2465
  tone: "error",
1843
2466
  status: terminalRunStatus,
@@ -1848,19 +2471,19 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1848
2471
  runId: input.runId,
1849
2472
  error: terminalFailureDetail
1850
2473
  });
1851
- throw new CliError2(terminalFailureDetail, exit.code ?? 1);
2474
+ throw new CliError(terminalFailureDetail, exit.code ?? 1);
1852
2475
  }
1853
2476
  if (planningClassification.planningRequired) {
1854
2477
  const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
1855
- const expectedPlanPath = resolve4(planWorkspace, planningArtifactPath);
1856
- if (!existsSync2(expectedPlanPath)) {
2478
+ const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
2479
+ if (!existsSync4(expectedPlanPath)) {
1857
2480
  const failedAt = new Date().toISOString();
1858
2481
  const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
1859
- patchAuthorityRun(context.projectRoot, input.runId, {
1860
- status: "needs_attention",
1861
- completedAt: failedAt,
2482
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2483
+ reason: "plan-artifact-missing",
1862
2484
  errorText: failureDetail
1863
2485
  });
2486
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1864
2487
  appendRunLog(context.projectRoot, input.runId, {
1865
2488
  id: `log:${input.runId}:plan-artifact-missing`,
1866
2489
  title: "Required plan artifact missing",
@@ -1870,8 +2493,66 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1870
2493
  createdAt: failedAt
1871
2494
  });
1872
2495
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
1873
- throw new CliError2(failureDetail, 1);
2496
+ throw new CliError(failureDetail, 1);
2497
+ }
2498
+ }
2499
+ if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
2500
+ appendPiStageLog({
2501
+ projectRoot: context.projectRoot,
2502
+ runId: input.runId,
2503
+ stage: "Validate",
2504
+ detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
2505
+ status: "completed"
2506
+ });
2507
+ if (verificationAction && !reviewStarted) {
2508
+ verificationAction.complete("Completion verification checks finished.");
2509
+ }
2510
+ if (!reviewAction) {
2511
+ promoteToReviewing("Server-owned closeout is queued.");
2512
+ }
2513
+ if (reviewAction) {
2514
+ reviewAction.complete("Provider work accepted; server-owned closeout requested.");
1874
2515
  }
2516
+ const requestedAt = new Date().toISOString();
2517
+ setRunStatusOrFail(context.projectRoot, input.runId, "closing-out", { reason: "server-closeout-requested" });
2518
+ patchAuthorityRun(context.projectRoot, input.runId, {
2519
+ completedAt: null,
2520
+ errorText: null,
2521
+ serverCloseout: {
2522
+ status: "pending",
2523
+ phase: "queued",
2524
+ updatedAt: requestedAt,
2525
+ runtimeWorkspace: latestRuntimeWorkspace,
2526
+ branch: latestRuntimeBranch,
2527
+ taskId: input.taskId ?? runtimeTaskId
2528
+ }
2529
+ });
2530
+ appendRunLog(context.projectRoot, input.runId, {
2531
+ id: `log:${input.runId}:server-closeout-requested`,
2532
+ title: "Server-owned closeout requested",
2533
+ detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
2534
+ tone: "info",
2535
+ status: "reviewing",
2536
+ createdAt: requestedAt,
2537
+ payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
2538
+ });
2539
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
2540
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "closing-out", detail: "Server-owned closeout requested.", ...sessionAnchor() });
2541
+ await context.emitEvent("command.finished", {
2542
+ command: [
2543
+ "rig",
2544
+ "server",
2545
+ "task-run",
2546
+ ...input.taskId ? ["--task", input.taskId] : [],
2547
+ ...input.title ? ["--title", input.title] : []
2548
+ ],
2549
+ formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
2550
+ exitCode: 0,
2551
+ durationMs: 0,
2552
+ startedAt,
2553
+ finishedAt: requestedAt
2554
+ });
2555
+ process.exit(0);
1875
2556
  }
1876
2557
  const runPiPrFeedbackFix = async (message) => {
1877
2558
  appendPiStageLog({
@@ -1900,11 +2581,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1900
2581
  child.stdin.write(message);
1901
2582
  }
1902
2583
  child.stdin.end();
2584
+ const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
2585
+ let feedbackAssistantText = "";
2586
+ const feedbackPendingToolUses = new Map;
1903
2587
  const stdout = createLineInterface({ input: child.stdout });
1904
2588
  stdout.on("line", (line) => {
1905
2589
  const trimmed = line.trim();
1906
2590
  if (!trimmed)
1907
2591
  return;
2592
+ try {
2593
+ const record = JSON.parse(trimmed);
2594
+ const providerLogs = buildClaudeLogsFromRecord({
2595
+ runId: input.runId,
2596
+ record,
2597
+ createdAtFallback: new Date().toISOString(),
2598
+ status: "reviewing",
2599
+ pendingToolUses: feedbackPendingToolUses
2600
+ });
2601
+ for (const providerLog of providerLogs) {
2602
+ appendRunLog(context.projectRoot, input.runId, providerLog);
2603
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
2604
+ emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2605
+ if (providerLog.title === "Tool activity")
2606
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2607
+ }
2608
+ const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
2609
+ projectRoot: context.projectRoot,
2610
+ runId: input.runId,
2611
+ messageId: feedbackAssistantMessageId,
2612
+ record,
2613
+ assistantText: feedbackAssistantText
2614
+ });
2615
+ const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
2616
+ if (hadAssistantDelta) {
2617
+ feedbackAssistantText = nextFeedbackAssistantText;
2618
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2619
+ }
2620
+ if (providerLogs.length > 0 || hadAssistantDelta)
2621
+ return;
2622
+ } catch {}
1908
2623
  appendRunLog(context.projectRoot, input.runId, {
1909
2624
  id: nextRunLogId(),
1910
2625
  title: "Pi PR feedback fix output",
@@ -1930,10 +2645,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1930
2645
  });
1931
2646
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
1932
2647
  });
1933
- const exitCode = await new Promise((resolve5) => {
1934
- child.once("error", () => resolve5(1));
1935
- child.once("close", (code) => resolve5(code ?? 1));
2648
+ const exitCode = await new Promise((resolve7) => {
2649
+ child.once("error", () => resolve7(1));
2650
+ child.once("close", (code) => resolve7(code ?? 1));
1936
2651
  });
2652
+ for (const pendingLog of flushPendingClaudeToolUseLogs({
2653
+ runId: input.runId,
2654
+ status: exitCode === 0 ? "completed" : "failed",
2655
+ pendingToolUses: feedbackPendingToolUses
2656
+ })) {
2657
+ appendRunLog(context.projectRoot, input.runId, pendingLog);
2658
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
2659
+ emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2660
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2661
+ }
2662
+ if (feedbackAssistantText.trim()) {
2663
+ appendRunTimeline(context.projectRoot, input.runId, {
2664
+ id: feedbackAssistantMessageId,
2665
+ type: "assistant_message",
2666
+ text: feedbackAssistantText,
2667
+ state: "completed",
2668
+ createdAt: new Date().toISOString(),
2669
+ completedAt: new Date().toISOString()
2670
+ });
2671
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2672
+ }
1937
2673
  if (exitCode !== 0) {
1938
2674
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
1939
2675
  }
@@ -1969,25 +2705,25 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1969
2705
  const failureDetail = postValidation.pr?.actionableFeedback.join(`
1970
2706
  `) || "PR automation needs attention.";
1971
2707
  const failedAt = new Date().toISOString();
1972
- patchAuthorityRun(context.projectRoot, input.runId, {
1973
- status: "needs_attention",
1974
- completedAt: failedAt,
2708
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2709
+ reason: "pr-automation-needs-attention",
1975
2710
  errorText: failureDetail
1976
2711
  });
2712
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1977
2713
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
1978
- throw new CliError2(failureDetail, 1);
2714
+ throw new CliError(failureDetail, 1);
1979
2715
  }
1980
2716
  } catch (error) {
1981
- if (error instanceof CliError2) {
2717
+ if (error instanceof CliError) {
1982
2718
  throw error;
1983
2719
  }
1984
2720
  const failureDetail = `Failed to complete PR/issue closeout for ${input.taskId ?? runtimeTaskId}: ${error instanceof Error ? error.message : String(error)}`;
1985
2721
  const failedAt = new Date().toISOString();
1986
- patchAuthorityRun(context.projectRoot, input.runId, {
1987
- status: "failed",
1988
- completedAt: failedAt,
2722
+ setRunStatusOrFail(context.projectRoot, input.runId, "failed", {
2723
+ reason: "pr-closeout-failed",
1989
2724
  errorText: failureDetail
1990
2725
  });
2726
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1991
2727
  appendRunLog(context.projectRoot, input.runId, {
1992
2728
  id: `log:${input.runId}:pr-closeout-failed`,
1993
2729
  title: "PR/issue closeout failed",
@@ -1997,7 +2733,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1997
2733
  createdAt: failedAt
1998
2734
  });
1999
2735
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
2000
- throw new CliError2(failureDetail, 1);
2736
+ throw new CliError(failureDetail, 1);
2001
2737
  }
2002
2738
  if (verificationAction && !reviewStarted) {
2003
2739
  verificationAction.complete("Completion verification checks finished.");
@@ -2008,10 +2744,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2008
2744
  if (reviewAction) {
2009
2745
  reviewAction.complete("Run marked completed.");
2010
2746
  }
2011
- patchAuthorityRun(context.projectRoot, input.runId, {
2012
- status: "completed",
2013
- completedAt: new Date().toISOString()
2014
- });
2747
+ setRunStatusOrFail(context.projectRoot, input.runId, "completed", { reason: "driver-completed" });
2015
2748
  emitServerRunEvent({ type: "completed", runId: input.runId });
2016
2749
  await context.emitEvent("command.finished", {
2017
2750
  command: [