@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90

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 (114) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/build-rig-binaries.d.ts +2 -0
  3. package/dist/bin/rig.d.ts +2 -0
  4. package/dist/bin/rig.js +11052 -6668
  5. package/dist/src/app/board.d.ts +23 -0
  6. package/dist/src/app/board.js +1786 -0
  7. package/dist/src/app/drone-ui.d.ts +37 -0
  8. package/dist/src/app/drone-ui.js +294 -0
  9. package/dist/src/app/theme.d.ts +47 -0
  10. package/dist/src/app/theme.js +150 -0
  11. package/dist/src/commands/_async-ui.d.ts +13 -0
  12. package/dist/src/commands/_async-ui.js +223 -0
  13. package/dist/src/commands/_authority-runs.d.ts +22 -0
  14. package/dist/src/commands/_authority-runs.js +2 -3
  15. package/dist/src/commands/_cli-format.d.ts +49 -0
  16. package/dist/src/commands/_cli-format.js +447 -0
  17. package/dist/src/commands/_connection-state.d.ts +44 -0
  18. package/dist/src/commands/_connection-state.js +30 -11
  19. package/dist/src/commands/_doctor-checks.d.ts +52 -0
  20. package/dist/src/commands/_doctor-checks.js +180 -44
  21. package/dist/src/commands/_help-catalog.d.ts +51 -0
  22. package/dist/src/commands/_help-catalog.js +442 -0
  23. package/dist/src/commands/_json-output.d.ts +11 -0
  24. package/dist/src/commands/_json-output.js +56 -0
  25. package/dist/src/commands/_operator-surface.d.ts +34 -0
  26. package/dist/src/commands/_operator-surface.js +220 -0
  27. package/dist/src/commands/_operator-view.d.ts +30 -0
  28. package/dist/src/commands/_operator-view.js +803 -73
  29. package/dist/src/commands/_parsers.d.ts +15 -0
  30. package/dist/src/commands/_parsers.js +18 -11
  31. package/dist/src/commands/_paths.d.ts +11 -0
  32. package/dist/src/commands/_pi-frontend.d.ts +27 -0
  33. package/dist/src/commands/_pi-frontend.js +742 -0
  34. package/dist/src/commands/_pi-install.d.ts +42 -0
  35. package/dist/src/commands/_pi-install.js +5 -4
  36. package/dist/src/commands/_policy.d.ts +8 -0
  37. package/dist/src/commands/_policy.js +12 -5
  38. package/dist/src/commands/_preflight.d.ts +22 -0
  39. package/dist/src/commands/_preflight.js +190 -128
  40. package/dist/src/commands/_probes.d.ts +1 -0
  41. package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
  42. package/dist/src/commands/_run-driver-helpers.js +75 -22
  43. package/dist/src/commands/_run-replay.d.ts +24 -0
  44. package/dist/src/commands/_run-replay.js +142 -0
  45. package/dist/src/commands/_server-client.d.ts +186 -0
  46. package/dist/src/commands/_server-client.js +360 -61
  47. package/dist/src/commands/_snapshot-upload.d.ts +39 -0
  48. package/dist/src/commands/_snapshot-upload.js +163 -39
  49. package/dist/src/commands/_spinner.d.ts +25 -0
  50. package/dist/src/commands/_spinner.js +65 -0
  51. package/dist/src/commands/_task-picker.d.ts +9 -0
  52. package/dist/src/commands/_task-picker.js +172 -19
  53. package/dist/src/commands/agent.d.ts +3 -0
  54. package/dist/src/commands/agent.js +39 -20
  55. package/dist/src/commands/browser.d.ts +65 -0
  56. package/dist/src/commands/browser.js +334 -51
  57. package/dist/src/commands/connect.d.ts +7 -0
  58. package/dist/src/commands/connect.js +272 -33
  59. package/dist/src/commands/dist.d.ts +28 -0
  60. package/dist/src/commands/dist.js +19 -12
  61. package/dist/src/commands/doctor.d.ts +3 -0
  62. package/dist/src/commands/doctor.js +378 -45
  63. package/dist/src/commands/github.d.ts +3 -0
  64. package/dist/src/commands/github.js +375 -53
  65. package/dist/src/commands/inbox.d.ts +30 -0
  66. package/dist/src/commands/inbox.js +760 -70
  67. package/dist/src/commands/init.d.ts +74 -0
  68. package/dist/src/commands/init.js +963 -119
  69. package/dist/src/commands/inspect.d.ts +3 -0
  70. package/dist/src/commands/inspect.js +588 -32
  71. package/dist/src/commands/inspector.d.ts +3 -0
  72. package/dist/src/commands/inspector.js +20 -13
  73. package/dist/src/commands/pi.d.ts +3 -0
  74. package/dist/src/commands/pi.js +177 -0
  75. package/dist/src/commands/plugin.d.ts +16 -0
  76. package/dist/src/commands/plugin.js +95 -27
  77. package/dist/src/commands/profile-and-review.d.ts +4 -0
  78. package/dist/src/commands/profile-and-review.js +26 -19
  79. package/dist/src/commands/queue.d.ts +3 -0
  80. package/dist/src/commands/queue.js +32 -12
  81. package/dist/src/commands/remote.d.ts +3 -0
  82. package/dist/src/commands/remote.js +43 -36
  83. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  84. package/dist/src/commands/repo-git-harness.js +22 -15
  85. package/dist/src/commands/run.d.ts +3 -0
  86. package/dist/src/commands/run.js +1379 -159
  87. package/dist/src/commands/server.d.ts +7 -0
  88. package/dist/src/commands/server.js +502 -57
  89. package/dist/src/commands/setup.d.ts +16 -0
  90. package/dist/src/commands/setup.js +390 -63
  91. package/dist/src/commands/stats.d.ts +12 -0
  92. package/dist/src/commands/stats.js +1051 -0
  93. package/dist/src/commands/task-report-bug.d.ts +19 -0
  94. package/dist/src/commands/task-report-bug.js +260 -62
  95. package/dist/src/commands/task-run-driver.d.ts +132 -0
  96. package/dist/src/commands/task-run-driver.js +886 -130
  97. package/dist/src/commands/task.d.ts +14 -0
  98. package/dist/src/commands/task.js +1644 -314
  99. package/dist/src/commands/test.d.ts +3 -0
  100. package/dist/src/commands/test.js +15 -8
  101. package/dist/src/commands/workspace.d.ts +3 -0
  102. package/dist/src/commands/workspace.js +18 -11
  103. package/dist/src/commands.d.ts +29 -0
  104. package/dist/src/commands.js +10967 -6637
  105. package/dist/src/index.d.ts +4 -0
  106. package/dist/src/index.js +10168 -5787
  107. package/dist/src/launcher.d.ts +61 -0
  108. package/dist/src/launcher.js +77 -13
  109. package/dist/src/report-bug.d.ts +44 -0
  110. package/dist/src/report-bug.js +3 -3
  111. package/dist/src/runner.d.ts +47 -0
  112. package/dist/src/runner.js +16 -22
  113. package/dist/src/withMutedConsole.d.ts +2 -0
  114. package/package.json +13 -6
@@ -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,324 @@ 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 rawDetail = diagnostics ?? (text || response.statusText);
732
+ const detail = diagnostics ? rawDetail : rawDetail.split(`
733
+ `).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
734
+ const failure = await buildServerFailureContext(context.projectRoot, server);
735
+ throw new CliError(`Rig server request failed (${response.status}): ${detail}
736
+ ${failure.contextLine}`, 1, { hint: failure.hint });
737
+ }
738
+ return payload;
739
+ }
740
+ async function updateWorkspaceTaskViaServer(context, input) {
741
+ const payload = await requestServerJson(context, "/api/tasks/update", {
742
+ method: "POST",
743
+ headers: { "content-type": "application/json" },
744
+ body: JSON.stringify(input)
745
+ });
746
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
747
+ }
748
+ var RESUMABLE_RUN_STATUSES = new Set([
749
+ "created",
750
+ "preparing",
751
+ "running",
752
+ "validating",
753
+ "reviewing",
754
+ "stopped",
755
+ "failed",
756
+ "needs-attention",
757
+ "needs_attention"
758
+ ]);
759
+
391
760
  // packages/cli/src/commands/task-run-driver.ts
392
761
  var PI_CANONICAL_RUN_STAGES = [
393
762
  "Connect",
@@ -412,6 +781,19 @@ function looksLikeGitHubToken(value) {
412
781
  return false;
413
782
  return /^(gh[opusr]_|github_pat_)/.test(token);
414
783
  }
784
+ function isSensitivePayloadKey(key) {
785
+ return /token|secret|password|authorization|credential/i.test(key);
786
+ }
787
+ function redactSensitivePayload(value) {
788
+ if (Array.isArray(value))
789
+ return value.map(redactSensitivePayload);
790
+ if (!value || typeof value !== "object")
791
+ return value;
792
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [
793
+ key,
794
+ isSensitivePayloadKey(key) && typeof child === "string" && child.trim() ? "[redacted]" : redactSensitivePayload(child)
795
+ ]));
796
+ }
415
797
  function githubBridgeEnv(token) {
416
798
  const clean = token?.trim();
417
799
  if (!clean)
@@ -431,6 +813,7 @@ function buildPiRigBridgeEnv(input) {
431
813
  RIG_SERVER_RUN_ID: input.runId,
432
814
  RIG_TASK_ID: input.taskId,
433
815
  RIG_RUNTIME_ADAPTER: "pi",
816
+ RIG_STEERING_POLL_MS: "0",
434
817
  ...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
435
818
  ...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
436
819
  ...githubBridgeEnv(githubToken)
@@ -455,12 +838,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
455
838
  return 0;
456
839
  let copied = 0;
457
840
  for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
458
- const sourcePath = resolve4(sourceRoot, relativePath);
459
- const targetPath = resolve4(targetRoot, relativePath);
841
+ const sourcePath = resolve6(sourceRoot, relativePath);
842
+ const targetPath = resolve6(targetRoot, relativePath);
460
843
  try {
461
844
  if (!statSync(sourcePath).isFile())
462
845
  continue;
463
- mkdirSync(resolve4(targetPath, ".."), { recursive: true });
846
+ mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
464
847
  copyFileSync(sourcePath, targetPath);
465
848
  copied += 1;
466
849
  } catch {}
@@ -499,7 +882,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
499
882
  return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
500
883
  return {
501
884
  RIG_BASELINE_MODE: "dirty-snapshot",
502
- RIG_DIRTY_BASELINE_READY_FILE: resolve4(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
885
+ RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
503
886
  };
504
887
  }
505
888
  function positiveInt(value, fallback) {
@@ -507,7 +890,7 @@ function positiveInt(value, fallback) {
507
890
  }
508
891
  function resolveTaskRunAutomationLimits(config, env = process.env) {
509
892
  const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
510
- const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 30);
893
+ const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
511
894
  return {
512
895
  maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
513
896
  maxPrFixIterations: configuredPrFixIterations
@@ -601,12 +984,6 @@ function appendPiStageLog(input) {
601
984
  });
602
985
  emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
603
986
  }
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
987
  function createCommandRunner(binary) {
611
988
  return async (args, options) => {
612
989
  const child = spawn(binary, [...args], {
@@ -617,9 +994,9 @@ function createCommandRunner(binary) {
617
994
  const stderrChunks = [];
618
995
  child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
619
996
  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({
997
+ return await new Promise((resolve7) => {
998
+ child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
999
+ child.once("close", (code) => resolve7({
623
1000
  exitCode: code ?? 1,
624
1001
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
625
1002
  stderr: Buffer.concat(stderrChunks).toString("utf8")
@@ -643,8 +1020,9 @@ async function runTaskRunPostValidationLifecycle(input) {
643
1020
  if (!taskId) {
644
1021
  return { status: "skipped" };
645
1022
  }
646
- const config = input.config ?? {};
647
- const prMode = config.pr?.mode ?? "auto";
1023
+ const configInput = input.config ?? null;
1024
+ const config = configInput ?? {};
1025
+ const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
648
1026
  if (prMode === "off" || prMode === "ask") {
649
1027
  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
1028
  input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
@@ -660,7 +1038,7 @@ async function runTaskRunPostValidationLifecycle(input) {
660
1038
  gitCommand
661
1039
  });
662
1040
  const prAutomation = input.prAutomation ?? runPrAutomation;
663
- const updateTaskSource = input.updateTaskSource ?? updateConfiguredTaskSourceTask;
1041
+ const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
664
1042
  const stage = input.appendStage ?? (() => {
665
1043
  return;
666
1044
  });
@@ -682,7 +1060,7 @@ async function runTaskRunPostValidationLifecycle(input) {
682
1060
  command: gitCommand
683
1061
  });
684
1062
  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");
1063
+ await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
686
1064
  stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
687
1065
  const pr = await prAutomation({
688
1066
  projectRoot: workspace,
@@ -692,7 +1070,9 @@ async function runTaskRunPostValidationLifecycle(input) {
692
1070
  config,
693
1071
  sourceTask: input.sourceTask,
694
1072
  uploadedSnapshot: input.uploadedSnapshot,
1073
+ artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
695
1074
  command: ghCommand,
1075
+ gitCommand,
696
1076
  steerPi,
697
1077
  lifecycle: {
698
1078
  onPrOpened: async ({ prUrl }) => {
@@ -741,8 +1121,6 @@ async function runTaskRunPostValidationLifecycle(input) {
741
1121
  runtimeWorkspace: input.runtimeWorkspace ?? null
742
1122
  })
743
1123
  }
744
- }).catch(() => {
745
- return;
746
1124
  });
747
1125
  }
748
1126
  },
@@ -761,8 +1139,6 @@ async function runTaskRunPostValidationLifecycle(input) {
761
1139
  runtimeWorkspace: input.runtimeWorkspace ?? null
762
1140
  })
763
1141
  }
764
- }).catch(() => {
765
- return;
766
1142
  });
767
1143
  }
768
1144
  },
@@ -791,8 +1167,17 @@ async function runTaskRunPostValidationLifecycle(input) {
791
1167
  errorText: detail
792
1168
  })
793
1169
  }
794
- }).catch(() => {
795
- return;
1170
+ }).catch((error) => {
1171
+ try {
1172
+ appendRunLog(input.projectRoot, input.runId, {
1173
+ id: `log:${input.runId}:task-source-needs-attention-update`,
1174
+ title: "Task source needs-attention update failed",
1175
+ detail: error instanceof Error ? error.message : String(error),
1176
+ tone: "error",
1177
+ status: "needs_attention",
1178
+ createdAt: new Date().toISOString()
1179
+ });
1180
+ } catch {}
796
1181
  });
797
1182
  }
798
1183
  return { status: "needs_attention", pr };
@@ -815,7 +1200,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
815
1200
  return null;
816
1201
  }
817
1202
  for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
818
- const summary = readJsonFile(resolve4(artifactDir, "validation-summary.json"), null);
1203
+ const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
819
1204
  if (!summary || summary.status !== "fail") {
820
1205
  continue;
821
1206
  }
@@ -888,7 +1273,7 @@ function parsePositiveInt(value, option, fallback) {
888
1273
  }
889
1274
  const parsed = Number.parseInt(value, 10);
890
1275
  if (!Number.isFinite(parsed) || parsed <= 0) {
891
- throw new CliError2(`Invalid ${option} value: ${value}`);
1276
+ throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
892
1277
  }
893
1278
  return parsed;
894
1279
  }
@@ -896,14 +1281,14 @@ function readTaskRunAcceptedArtifactState(input) {
896
1281
  if (!input.taskId || !input.workspaceDir) {
897
1282
  return { accepted: false, reason: null };
898
1283
  }
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");
1284
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1285
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1286
+ const taskResultPath = resolve6(artifactDir, "task-result.json");
902
1287
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
903
1288
  if (reviewStatus !== "APPROVED") {
904
1289
  return { accepted: false, reason: null };
905
1290
  }
906
- const taskResult = readJsonFile(taskResultPath, null);
1291
+ const taskResult = readJsonFile2(taskResultPath, null);
907
1292
  if (taskResult?.status !== "completed") {
908
1293
  return { accepted: false, reason: null };
909
1294
  }
@@ -935,13 +1320,13 @@ function resolveTaskRunRetryContext(input) {
935
1320
  if (!input.taskId || !input.workspaceDir) {
936
1321
  return { shouldRetry: false, failureDetail: null, nextPrompt: null };
937
1322
  }
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);
1323
+ const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
1324
+ const reviewStatePath = resolve6(artifactDir, "review-state.json");
1325
+ const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
1326
+ const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
1327
+ const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
1328
+ const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
1329
+ const reviewState = readJsonFile2(reviewStatePath, null);
945
1330
  const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
946
1331
  const reviewRejected = isTaskRunReviewRejected(reviewState);
947
1332
  if (reviewStatus === "APPROVED") {
@@ -994,12 +1379,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
994
1379
  }
995
1380
  return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
996
1381
  }
1382
+ function appendAssistantTimelineFromRecord(input) {
1383
+ let nextAssistantText = input.assistantText;
1384
+ if (input.record.type === "message_update") {
1385
+ const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
1386
+ if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
1387
+ nextAssistantText += assistantMessageEvent.delta;
1388
+ }
1389
+ } else if (input.record.type === "stream_event") {
1390
+ const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
1391
+ const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
1392
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
1393
+ nextAssistantText += delta.text;
1394
+ }
1395
+ } else if (input.record.type === "assistant") {
1396
+ const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
1397
+ const content = Array.isArray(message.content) ? message.content : [];
1398
+ const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
1399
+ if (fullText.length > nextAssistantText.length)
1400
+ nextAssistantText = fullText;
1401
+ }
1402
+ if (nextAssistantText !== input.assistantText) {
1403
+ appendRunTimeline(input.projectRoot, input.runId, {
1404
+ id: input.messageId,
1405
+ type: "assistant_message",
1406
+ text: nextAssistantText,
1407
+ state: "streaming",
1408
+ createdAt: new Date().toISOString()
1409
+ });
1410
+ }
1411
+ return nextAssistantText;
1412
+ }
1413
+ function appendPiRpcProtocolLogFromRecord(input) {
1414
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1415
+ if (type === "response") {
1416
+ const command = typeof input.record.command === "string" ? input.record.command : "rpc";
1417
+ const success = input.record.success !== false;
1418
+ if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
1419
+ return true;
1420
+ }
1421
+ appendRunLog(input.projectRoot, input.runId, {
1422
+ id: input.nextRunLogId(),
1423
+ title: success ? "Pi RPC response" : "Pi RPC error",
1424
+ detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
1425
+ tone: success ? "tool" : "error",
1426
+ status: input.status,
1427
+ payload: input.record,
1428
+ createdAt: new Date().toISOString()
1429
+ });
1430
+ emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
1431
+ return true;
1432
+ }
1433
+ if (type !== "extension_ui_request")
1434
+ return false;
1435
+ const method = typeof input.record.method === "string" ? input.record.method : "ui";
1436
+ let title = "Pi UI event";
1437
+ let detail = method;
1438
+ let tone = "info";
1439
+ if (method === "notify") {
1440
+ title = "Pi notification";
1441
+ detail = String(input.record.message ?? "");
1442
+ tone = input.record.notifyType === "error" ? "error" : "info";
1443
+ } else if (method === "setStatus") {
1444
+ title = "Pi UI status";
1445
+ detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
1446
+ tone = "tool";
1447
+ } else if (method === "setWidget") {
1448
+ title = "Pi UI widget";
1449
+ const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
1450
+ detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
1451
+ tone = "tool";
1452
+ } else if (method === "setTitle") {
1453
+ title = "Pi UI title";
1454
+ detail = String(input.record.title ?? "");
1455
+ tone = "tool";
1456
+ } else if (method === "set_editor_text") {
1457
+ title = "Pi editor update";
1458
+ detail = String(input.record.text ?? "").slice(0, 500);
1459
+ tone = "tool";
1460
+ } else {
1461
+ title = "Pi UI request";
1462
+ detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
1463
+ }
1464
+ appendRunLog(input.projectRoot, input.runId, {
1465
+ id: input.nextRunLogId(),
1466
+ title,
1467
+ detail,
1468
+ tone,
1469
+ status: input.status,
1470
+ payload: input.record,
1471
+ createdAt: new Date().toISOString()
1472
+ });
1473
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
1474
+ return true;
1475
+ }
1476
+ function appendPiToolTimelineFromRecord(input) {
1477
+ const type = typeof input.record.type === "string" ? input.record.type : "";
1478
+ if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
1479
+ return false;
1480
+ const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
1481
+ const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
1482
+ const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
1483
+ appendRunTimeline(input.projectRoot, input.runId, {
1484
+ id: `tool:${toolCallId}:${type}`,
1485
+ type,
1486
+ toolName,
1487
+ status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
1488
+ createdAt: new Date().toISOString()
1489
+ });
1490
+ return true;
1491
+ }
1492
+ function isNonRenderablePiProtocolRecord(record) {
1493
+ const type = typeof record.type === "string" ? record.type : "";
1494
+ return type === "pi.event" || type === "status.update" || type === "activity.update" || type === "ready" || 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");
1495
+ }
1496
+ function appendToolTimelineFromLog(input) {
1497
+ const title = typeof input.log.title === "string" ? input.log.title : "";
1498
+ if (title !== "Tool activity")
1499
+ return;
1500
+ const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
1501
+ 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;
1502
+ const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
1503
+ appendRunTimeline(input.projectRoot, input.runId, {
1504
+ id: `tool:${logId}`,
1505
+ type: "tool_execution_update",
1506
+ toolName,
1507
+ status: typeof input.log.status === "string" ? input.log.status : "running",
1508
+ detail: typeof input.log.detail === "string" ? input.log.detail : null,
1509
+ payload,
1510
+ createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
1511
+ });
1512
+ }
997
1513
  function readTaskRunReviewStatus(reviewStatusPath) {
998
- if (!existsSync2(reviewStatusPath)) {
1514
+ if (!existsSync4(reviewStatusPath)) {
999
1515
  return null;
1000
1516
  }
1001
1517
  try {
1002
- const status = readFileSync2(reviewStatusPath, "utf8").trim().toUpperCase();
1518
+ const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
1003
1519
  return status === "APPROVED" || status === "REJECTED" ? status : null;
1004
1520
  } catch {
1005
1521
  return null;
@@ -1017,7 +1533,38 @@ function isTaskRunReviewRejected(reviewState) {
1017
1533
  function runSourceTaskIdentity(sourceTask) {
1018
1534
  return sourceTask;
1019
1535
  }
1020
- async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateConfiguredTaskSourceTask) {
1536
+ function sourceTaskIssueNodeId(sourceTask) {
1537
+ if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
1538
+ return null;
1539
+ const record = sourceTask;
1540
+ const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
1541
+ if (direct?.trim())
1542
+ return direct.trim();
1543
+ const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
1544
+ return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
1545
+ }
1546
+ var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
1547
+ const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
1548
+ id: input.taskId,
1549
+ ...input.update.status ? { status: input.update.status } : {},
1550
+ ...input.update.comment ? { comment: input.update.comment } : {},
1551
+ ...input.update.title ? { title: input.update.title } : {},
1552
+ ...typeof input.update.body === "string" ? { body: input.update.body } : {},
1553
+ issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
1554
+ });
1555
+ if (serverResult.ok === false) {
1556
+ throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
1557
+ }
1558
+ return {
1559
+ updated: serverResult.ok !== false,
1560
+ taskId: input.taskId,
1561
+ status: input.update.status,
1562
+ source: "server",
1563
+ sourceKind: "server",
1564
+ projectSync: serverResult.projectSync
1565
+ };
1566
+ };
1567
+ async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
1021
1568
  if (!taskId)
1022
1569
  return;
1023
1570
  const config = await loadTaskRunAutomationConfig(projectRoot);
@@ -1081,9 +1628,14 @@ function stringArrayField(record, key) {
1081
1628
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
1082
1629
  }
1083
1630
  async function executeRigOwnedTaskRun(context, input) {
1631
+ process.stdout.on("error", () => {});
1632
+ process.stderr.on("error", () => {});
1633
+ configureRunEventSink(context.projectRoot);
1084
1634
  const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
1085
1635
  const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
1086
1636
  const resumeMode = process.env.RIG_RUN_RESUME === "1";
1637
+ let latestPiSessionId = typeof existingRunRecord?.piSession?.sessionId === "string" && existingRunRecord.piSession.sessionId.trim() ? existingRunRecord.piSession.sessionId : null;
1638
+ const sessionAnchor = () => latestPiSessionId ? { sessionId: latestPiSessionId } : {};
1087
1639
  const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
1088
1640
  const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
1089
1641
  let prompt = buildRunPrompt({
@@ -1107,11 +1659,7 @@ async function executeRigOwnedTaskRun(context, input) {
1107
1659
  ...input.model ? ["--model", input.model] : [],
1108
1660
  "--prompt"
1109
1661
  ] : input.runtimeAdapter === "pi" ? [
1110
- "--print",
1111
- "--verbose",
1112
- "--mode",
1113
- "json",
1114
- "--no-session",
1662
+ "__rig_pi_session_daemon__",
1115
1663
  ...input.model ? ["--model", input.model] : []
1116
1664
  ] : [
1117
1665
  "--print",
@@ -1142,8 +1690,8 @@ async function executeRigOwnedTaskRun(context, input) {
1142
1690
  runtimeAdapter: input.runtimeAdapter,
1143
1691
  status: resumeMode ? "preparing" : "created"
1144
1692
  });
1693
+ setRunStatusOrFail(context.projectRoot, input.runId, "preparing", { reason: "driver-start" });
1145
1694
  patchAuthorityRun(context.projectRoot, input.runId, {
1146
- status: "preparing",
1147
1695
  startedAt,
1148
1696
  completedAt: null,
1149
1697
  errorText: null,
@@ -1160,7 +1708,8 @@ async function executeRigOwnedTaskRun(context, input) {
1160
1708
  type: "status",
1161
1709
  runId: input.runId,
1162
1710
  status: "preparing",
1163
- detail: input.taskId ?? input.title ?? runtimeTaskId
1711
+ detail: input.taskId ?? input.title ?? runtimeTaskId,
1712
+ ...sessionAnchor()
1164
1713
  });
1165
1714
  appendRunLog(context.projectRoot, input.runId, {
1166
1715
  id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
@@ -1186,15 +1735,15 @@ async function executeRigOwnedTaskRun(context, input) {
1186
1735
  const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
1187
1736
  const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
1188
1737
  const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
1189
- const planningArtifactPath = resolve4("artifacts", runtimeTaskId, "implementation-plan.md");
1738
+ const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
1190
1739
  const persistedPlanning = {
1191
1740
  ...planningClassification,
1192
1741
  classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
1193
1742
  artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
1194
1743
  classifiedAt: new Date().toISOString()
1195
1744
  };
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)}
1745
+ mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
1746
+ writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
1198
1747
  `, "utf8");
1199
1748
  patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
1200
1749
  prompt = `${prompt}
@@ -1208,7 +1757,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1208
1757
  projectRoot: context.projectRoot,
1209
1758
  runId: input.runId,
1210
1759
  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,
1760
+ 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
1761
  status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
1213
1762
  });
1214
1763
  }
@@ -1244,11 +1793,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1244
1793
  let verificationStarted = false;
1245
1794
  let reviewStarted = false;
1246
1795
  let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
1247
- let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve4(existingRunRecord.sessionPath, "..") : null;
1796
+ let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
1248
1797
  let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
1249
1798
  let latestProviderCommand = null;
1250
1799
  let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
1251
1800
  let snapshotSidecarPromise = null;
1801
+ let wrapperManagesRuntimeSnapshot = false;
1252
1802
  let dirtyBaselineApplied = false;
1253
1803
  const childEnv = {
1254
1804
  ...process.env,
@@ -1279,8 +1829,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1279
1829
  if (verificationStarted)
1280
1830
  return;
1281
1831
  verificationStarted = true;
1282
- patchAuthorityRun(context.projectRoot, input.runId, { status: "validating" });
1283
- emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null });
1832
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating");
1833
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null, ...sessionAnchor() });
1284
1834
  verificationAction = startRunAction(context.projectRoot, input.runId, {
1285
1835
  actionId: `action:${input.runId}:completion-verification`,
1286
1836
  actionType: "completion-verification",
@@ -1292,8 +1842,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1292
1842
  if (reviewStarted)
1293
1843
  return;
1294
1844
  reviewStarted = true;
1295
- patchAuthorityRun(context.projectRoot, input.runId, { status: "reviewing" });
1296
- emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null });
1845
+ setRunStatusOrFail(context.projectRoot, input.runId, "reviewing");
1846
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null, ...sessionAnchor() });
1297
1847
  reviewAction = startRunAction(context.projectRoot, input.runId, {
1298
1848
  actionId: `action:${input.runId}:review-closeout`,
1299
1849
  actionType: "run.review",
@@ -1301,6 +1851,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1301
1851
  detail: detail ?? "Verifier review is running."
1302
1852
  });
1303
1853
  };
1854
+ const nextRunLogId = createRunLogIdFactory(input.runId);
1304
1855
  const handleWrapperEvent = (rawPayload) => {
1305
1856
  let event = null;
1306
1857
  try {
@@ -1316,6 +1867,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1316
1867
  latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
1317
1868
  latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
1318
1869
  const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
1870
+ wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
1319
1871
  latestRuntimeBranch = runtimeId;
1320
1872
  provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
1321
1873
  runtimeId: runtimeId ?? null,
@@ -1324,13 +1876,13 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1324
1876
  logsDir: latestLogsDir,
1325
1877
  contextFile: payload.contextFile ?? null
1326
1878
  });
1879
+ setRunStatusOrFail(context.projectRoot, input.runId, "running");
1327
1880
  patchAuthorityRun(context.projectRoot, input.runId, {
1328
- status: "running",
1329
1881
  worktreePath: latestRuntimeWorkspace,
1330
- artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve4(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1882
+ artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
1331
1883
  logRoot: latestLogsDir,
1332
- sessionPath: latestSessionDir ? resolve4(latestSessionDir, "session.json") : null,
1333
- sessionLogPath: latestLogsDir ? resolve4(latestLogsDir, "agent-stdout.log") : null,
1884
+ sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
1885
+ sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
1334
1886
  branch: runtimeId
1335
1887
  });
1336
1888
  if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
@@ -1338,8 +1890,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1338
1890
  const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
1339
1891
  const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
1340
1892
  if (readyFile) {
1341
- mkdirSync(resolve4(readyFile, ".."), { recursive: true });
1342
- writeFileSync(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1893
+ mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
1894
+ writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
1343
1895
  `, "utf8");
1344
1896
  }
1345
1897
  appendRunLog(context.projectRoot, input.runId, {
@@ -1353,7 +1905,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1353
1905
  });
1354
1906
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
1355
1907
  }
1356
- if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1908
+ if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
1357
1909
  snapshotSidecarPromise = (async () => {
1358
1910
  const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
1359
1911
  projectRoot: context.projectRoot,
@@ -1381,7 +1933,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1381
1933
  type: "status",
1382
1934
  runId: input.runId,
1383
1935
  status: "running",
1384
- detail: latestRuntimeWorkspace
1936
+ detail: latestRuntimeWorkspace,
1937
+ ...sessionAnchor()
1385
1938
  });
1386
1939
  return true;
1387
1940
  }
@@ -1435,9 +1988,79 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1435
1988
  }
1436
1989
  return true;
1437
1990
  }
1438
- return false;
1991
+ 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") {
1992
+ 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";
1993
+ 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");
1994
+ appendRunLog(context.projectRoot, input.runId, {
1995
+ id: nextRunLogId(),
1996
+ title,
1997
+ detail,
1998
+ tone: event.type === "pi.session.error" ? "error" : "info",
1999
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
2000
+ payload: {
2001
+ eventType: event.type,
2002
+ runId: typeof payload.runId === "string" ? payload.runId : null,
2003
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
2004
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
2005
+ },
2006
+ createdAt: new Date().toISOString()
2007
+ });
2008
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
2009
+ return true;
2010
+ }
2011
+ if (event.type === "pi.session.ready") {
2012
+ const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
2013
+ const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
2014
+ if (privateMetadata) {
2015
+ patchAuthorityRun(context.projectRoot, input.runId, {
2016
+ piSession: publicMetadata,
2017
+ piSessionPrivate: privateMetadata
2018
+ });
2019
+ const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
2020
+ if (sessionId) {
2021
+ latestPiSessionId = sessionId;
2022
+ }
2023
+ appendRunLog(context.projectRoot, input.runId, {
2024
+ id: `log:${input.runId}:pi-session-ready`,
2025
+ title: "Worker Pi session ready",
2026
+ detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
2027
+ tone: "info",
2028
+ status: "running",
2029
+ payload: {
2030
+ sessionId: sessionId || null,
2031
+ runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
2032
+ },
2033
+ createdAt: new Date().toISOString()
2034
+ });
2035
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
2036
+ }
2037
+ return true;
2038
+ }
2039
+ 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") {
2040
+ 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";
2041
+ 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")}`;
2042
+ appendRunLog(context.projectRoot, input.runId, {
2043
+ id: nextRunLogId(),
2044
+ title,
2045
+ detail,
2046
+ tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
2047
+ status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
2048
+ payload,
2049
+ createdAt: new Date().toISOString()
2050
+ });
2051
+ emitServerRunEvent({ type: "log", runId: input.runId, title });
2052
+ return true;
2053
+ }
2054
+ appendRunTimeline(context.projectRoot, input.runId, {
2055
+ id: `timeline:${input.runId}:${Date.now()}:wrapper:${event.type}`,
2056
+ type: "wrapper-event",
2057
+ eventType: event.type,
2058
+ payload: redactSensitivePayload(payload),
2059
+ createdAt: new Date().toISOString()
2060
+ });
2061
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2062
+ return true;
1439
2063
  };
1440
- const nextRunLogId = createRunLogIdFactory(input.runId);
1441
2064
  const handleAgentStdoutLine = (line) => {
1442
2065
  const trimmed = line.trim();
1443
2066
  if (!trimmed)
@@ -1471,6 +2094,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1471
2094
  try {
1472
2095
  const record = JSON.parse(trimmed);
1473
2096
  const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
2097
+ if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
2098
+ projectRoot: context.projectRoot,
2099
+ runId: input.runId,
2100
+ record,
2101
+ status: liveLogStatus,
2102
+ nextRunLogId
2103
+ })) {
2104
+ return;
2105
+ }
2106
+ if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
2107
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2108
+ return;
2109
+ }
1474
2110
  const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
1475
2111
  runId: input.runId,
1476
2112
  record,
@@ -1487,7 +2123,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1487
2123
  if (providerLogs.length > 0) {
1488
2124
  for (const providerLog of providerLogs) {
1489
2125
  appendRunLog(context.projectRoot, input.runId, providerLog);
2126
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
1490
2127
  emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2128
+ if (providerLog.title === "Tool activity")
2129
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1491
2130
  }
1492
2131
  }
1493
2132
  if (input.runtimeAdapter === "codex") {
@@ -1539,6 +2178,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1539
2178
  return;
1540
2179
  }
1541
2180
  }
2181
+ if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
2182
+ return;
2183
+ }
1542
2184
  if (record.type === "assistant") {
1543
2185
  const message = record.message && typeof record.message === "object" ? record.message : record;
1544
2186
  const content = Array.isArray(message.content) ? message.content : [];
@@ -1645,7 +2287,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1645
2287
  let acceptedArtifactObservedAt = null;
1646
2288
  let acceptedArtifactPollTimer = null;
1647
2289
  let acceptedArtifactKillTimer = null;
1648
- const attemptExit = await new Promise((resolve5) => {
2290
+ const attemptExit = await new Promise((resolve7) => {
1649
2291
  let settled = false;
1650
2292
  const settle = (result) => {
1651
2293
  if (settled)
@@ -1653,7 +2295,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1653
2295
  settled = true;
1654
2296
  if (acceptedArtifactPollTimer)
1655
2297
  clearInterval(acceptedArtifactPollTimer);
1656
- resolve5(result);
2298
+ resolve7(result);
1657
2299
  };
1658
2300
  const pollAcceptedArtifacts = () => {
1659
2301
  const artifactState = readTaskRunAcceptedArtifactState({
@@ -1726,11 +2368,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1726
2368
  });
1727
2369
  for (const pendingLog of pendingLogs) {
1728
2370
  appendRunLog(context.projectRoot, input.runId, pendingLog);
2371
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
1729
2372
  emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2373
+ if (pendingLog.title === "Tool activity")
2374
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
1730
2375
  }
1731
2376
  process.off("SIGTERM", forwardSigterm);
1732
2377
  if (attemptExit.error) {
1733
- throw new CliError2(`Task run failed to start: ${attemptExit.error.message}`, 1);
2378
+ throw new CliError(`Task run failed to start: ${attemptExit.error.message}`, 1);
1734
2379
  }
1735
2380
  const retryContext = resolveTaskRunRetryContext({
1736
2381
  projectRoot: context.projectRoot,
@@ -1752,8 +2397,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1752
2397
  currentPrompt = [retryContext.nextPrompt, "", "Rig Pi steering message:", retrySteering].join(`
1753
2398
  `);
1754
2399
  reviewFailureDetail = failureDetail;
2400
+ setRunStatusOrFail(context.projectRoot, input.runId, "validating", { reason: "validation-retry" });
1755
2401
  patchAuthorityRun(context.projectRoot, input.runId, {
1756
- status: "validating",
1757
2402
  completedAt: null,
1758
2403
  errorText: null
1759
2404
  });
@@ -1777,7 +2422,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1777
2422
  type: "status",
1778
2423
  runId: input.runId,
1779
2424
  status: "validating",
1780
- detail: failureDetail
2425
+ detail: failureDetail,
2426
+ ...sessionAnchor()
1781
2427
  });
1782
2428
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Validate failed" });
1783
2429
  emitServerRunEvent({ type: "log", runId: input.runId, title: `Steering Pi to retry validation (attempt ${attempt + 1}/${maxAttempts})` });
@@ -1788,10 +2434,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1788
2434
  break;
1789
2435
  }
1790
2436
  if (!exit) {
1791
- throw new CliError2(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
2437
+ throw new CliError(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
1792
2438
  }
1793
2439
  if (exit.error) {
1794
- throw new CliError2(`Task run failed to start: ${exit.error.message}`, 1);
2440
+ throw new CliError(`Task run failed to start: ${exit.error.message}`, 1);
1795
2441
  }
1796
2442
  if (assistantText.trim()) {
1797
2443
  appendRunTimeline(context.projectRoot, input.runId, {
@@ -1818,9 +2464,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1818
2464
  reviewAction.fail(failureDetail);
1819
2465
  }
1820
2466
  let terminalFailureDetail = failureDetail;
1821
- const terminalRunStatus = reviewFailureDetail ? "needs_attention" : "failed";
2467
+ const terminalRunStatus = reviewFailureDetail ? "needs-attention" : "failed";
1822
2468
  try {
1823
- await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs_attention" ? "Rig task run needs attention." : "Rig task run failed.", {
2469
+ await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs-attention" ? "Rig task run needs attention." : "Rig task run failed.", {
1824
2470
  latestRuntimeWorkspace,
1825
2471
  latestLogsDir,
1826
2472
  latestSessionDir,
@@ -1830,14 +2476,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
1830
2476
  terminalFailureDetail = `${failureDetail}
1831
2477
  Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${error instanceof Error ? error.message : String(error)}`;
1832
2478
  }
1833
- patchAuthorityRun(context.projectRoot, input.runId, {
1834
- status: terminalRunStatus,
1835
- completedAt: failedAt,
2479
+ setRunStatusOrFail(context.projectRoot, input.runId, terminalRunStatus, {
2480
+ reason: "driver-terminal-failure",
1836
2481
  errorText: terminalFailureDetail
1837
2482
  });
2483
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1838
2484
  appendRunLog(context.projectRoot, input.runId, {
1839
2485
  id: `log:${input.runId}:${terminalRunStatus}`,
1840
- title: terminalRunStatus === "needs_attention" ? "Task run needs attention" : "Task run failed",
2486
+ title: terminalRunStatus === "needs-attention" ? "Task run needs attention" : "Task run failed",
1841
2487
  detail: terminalFailureDetail,
1842
2488
  tone: "error",
1843
2489
  status: terminalRunStatus,
@@ -1848,19 +2494,19 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1848
2494
  runId: input.runId,
1849
2495
  error: terminalFailureDetail
1850
2496
  });
1851
- throw new CliError2(terminalFailureDetail, exit.code ?? 1);
2497
+ throw new CliError(terminalFailureDetail, exit.code ?? 1);
1852
2498
  }
1853
2499
  if (planningClassification.planningRequired) {
1854
2500
  const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
1855
- const expectedPlanPath = resolve4(planWorkspace, planningArtifactPath);
1856
- if (!existsSync2(expectedPlanPath)) {
2501
+ const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
2502
+ if (!existsSync4(expectedPlanPath)) {
1857
2503
  const failedAt = new Date().toISOString();
1858
2504
  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,
2505
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2506
+ reason: "plan-artifact-missing",
1862
2507
  errorText: failureDetail
1863
2508
  });
2509
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1864
2510
  appendRunLog(context.projectRoot, input.runId, {
1865
2511
  id: `log:${input.runId}:plan-artifact-missing`,
1866
2512
  title: "Required plan artifact missing",
@@ -1870,8 +2516,66 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1870
2516
  createdAt: failedAt
1871
2517
  });
1872
2518
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
1873
- throw new CliError2(failureDetail, 1);
2519
+ throw new CliError(failureDetail, 1);
2520
+ }
2521
+ }
2522
+ if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
2523
+ appendPiStageLog({
2524
+ projectRoot: context.projectRoot,
2525
+ runId: input.runId,
2526
+ stage: "Validate",
2527
+ detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
2528
+ status: "completed"
2529
+ });
2530
+ if (verificationAction && !reviewStarted) {
2531
+ verificationAction.complete("Completion verification checks finished.");
2532
+ }
2533
+ if (!reviewAction) {
2534
+ promoteToReviewing("Server-owned closeout is queued.");
1874
2535
  }
2536
+ if (reviewAction) {
2537
+ reviewAction.complete("Provider work accepted; server-owned closeout requested.");
2538
+ }
2539
+ const requestedAt = new Date().toISOString();
2540
+ setRunStatusOrFail(context.projectRoot, input.runId, "closing-out", { reason: "server-closeout-requested" });
2541
+ patchAuthorityRun(context.projectRoot, input.runId, {
2542
+ completedAt: null,
2543
+ errorText: null,
2544
+ serverCloseout: {
2545
+ status: "pending",
2546
+ phase: "queued",
2547
+ updatedAt: requestedAt,
2548
+ runtimeWorkspace: latestRuntimeWorkspace,
2549
+ branch: latestRuntimeBranch,
2550
+ taskId: input.taskId ?? runtimeTaskId
2551
+ }
2552
+ });
2553
+ appendRunLog(context.projectRoot, input.runId, {
2554
+ id: `log:${input.runId}:server-closeout-requested`,
2555
+ title: "Server-owned closeout requested",
2556
+ detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
2557
+ tone: "info",
2558
+ status: "reviewing",
2559
+ createdAt: requestedAt,
2560
+ payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
2561
+ });
2562
+ emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
2563
+ emitServerRunEvent({ type: "status", runId: input.runId, status: "closing-out", detail: "Server-owned closeout requested.", ...sessionAnchor() });
2564
+ await context.emitEvent("command.finished", {
2565
+ command: [
2566
+ "rig",
2567
+ "server",
2568
+ "task-run",
2569
+ ...input.taskId ? ["--task", input.taskId] : [],
2570
+ ...input.title ? ["--title", input.title] : []
2571
+ ],
2572
+ formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
2573
+ exitCode: 0,
2574
+ durationMs: 0,
2575
+ startedAt,
2576
+ finishedAt: requestedAt
2577
+ });
2578
+ process.exit(0);
1875
2579
  }
1876
2580
  const runPiPrFeedbackFix = async (message) => {
1877
2581
  appendPiStageLog({
@@ -1900,11 +2604,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1900
2604
  child.stdin.write(message);
1901
2605
  }
1902
2606
  child.stdin.end();
2607
+ const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
2608
+ let feedbackAssistantText = "";
2609
+ const feedbackPendingToolUses = new Map;
1903
2610
  const stdout = createLineInterface({ input: child.stdout });
1904
2611
  stdout.on("line", (line) => {
1905
2612
  const trimmed = line.trim();
1906
2613
  if (!trimmed)
1907
2614
  return;
2615
+ try {
2616
+ const record = JSON.parse(trimmed);
2617
+ const providerLogs = buildClaudeLogsFromRecord({
2618
+ runId: input.runId,
2619
+ record,
2620
+ createdAtFallback: new Date().toISOString(),
2621
+ status: "reviewing",
2622
+ pendingToolUses: feedbackPendingToolUses
2623
+ });
2624
+ for (const providerLog of providerLogs) {
2625
+ appendRunLog(context.projectRoot, input.runId, providerLog);
2626
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
2627
+ emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
2628
+ if (providerLog.title === "Tool activity")
2629
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2630
+ }
2631
+ const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
2632
+ projectRoot: context.projectRoot,
2633
+ runId: input.runId,
2634
+ messageId: feedbackAssistantMessageId,
2635
+ record,
2636
+ assistantText: feedbackAssistantText
2637
+ });
2638
+ const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
2639
+ if (hadAssistantDelta) {
2640
+ feedbackAssistantText = nextFeedbackAssistantText;
2641
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2642
+ }
2643
+ if (providerLogs.length > 0 || hadAssistantDelta)
2644
+ return;
2645
+ } catch {}
1908
2646
  appendRunLog(context.projectRoot, input.runId, {
1909
2647
  id: nextRunLogId(),
1910
2648
  title: "Pi PR feedback fix output",
@@ -1930,10 +2668,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1930
2668
  });
1931
2669
  emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
1932
2670
  });
1933
- const exitCode = await new Promise((resolve5) => {
1934
- child.once("error", () => resolve5(1));
1935
- child.once("close", (code) => resolve5(code ?? 1));
2671
+ const exitCode = await new Promise((resolve7) => {
2672
+ child.once("error", () => resolve7(1));
2673
+ child.once("close", (code) => resolve7(code ?? 1));
1936
2674
  });
2675
+ for (const pendingLog of flushPendingClaudeToolUseLogs({
2676
+ runId: input.runId,
2677
+ status: exitCode === 0 ? "completed" : "failed",
2678
+ pendingToolUses: feedbackPendingToolUses
2679
+ })) {
2680
+ appendRunLog(context.projectRoot, input.runId, pendingLog);
2681
+ appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
2682
+ emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
2683
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2684
+ }
2685
+ if (feedbackAssistantText.trim()) {
2686
+ appendRunTimeline(context.projectRoot, input.runId, {
2687
+ id: feedbackAssistantMessageId,
2688
+ type: "assistant_message",
2689
+ text: feedbackAssistantText,
2690
+ state: "completed",
2691
+ createdAt: new Date().toISOString(),
2692
+ completedAt: new Date().toISOString()
2693
+ });
2694
+ emitServerRunEvent({ type: "timeline", runId: input.runId });
2695
+ }
1937
2696
  if (exitCode !== 0) {
1938
2697
  throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
1939
2698
  }
@@ -1969,25 +2728,25 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1969
2728
  const failureDetail = postValidation.pr?.actionableFeedback.join(`
1970
2729
  `) || "PR automation needs attention.";
1971
2730
  const failedAt = new Date().toISOString();
1972
- patchAuthorityRun(context.projectRoot, input.runId, {
1973
- status: "needs_attention",
1974
- completedAt: failedAt,
2731
+ setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
2732
+ reason: "pr-automation-needs-attention",
1975
2733
  errorText: failureDetail
1976
2734
  });
2735
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1977
2736
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
1978
- throw new CliError2(failureDetail, 1);
2737
+ throw new CliError(failureDetail, 1);
1979
2738
  }
1980
2739
  } catch (error) {
1981
- if (error instanceof CliError2) {
2740
+ if (error instanceof CliError) {
1982
2741
  throw error;
1983
2742
  }
1984
2743
  const failureDetail = `Failed to complete PR/issue closeout for ${input.taskId ?? runtimeTaskId}: ${error instanceof Error ? error.message : String(error)}`;
1985
2744
  const failedAt = new Date().toISOString();
1986
- patchAuthorityRun(context.projectRoot, input.runId, {
1987
- status: "failed",
1988
- completedAt: failedAt,
2745
+ setRunStatusOrFail(context.projectRoot, input.runId, "failed", {
2746
+ reason: "pr-closeout-failed",
1989
2747
  errorText: failureDetail
1990
2748
  });
2749
+ patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
1991
2750
  appendRunLog(context.projectRoot, input.runId, {
1992
2751
  id: `log:${input.runId}:pr-closeout-failed`,
1993
2752
  title: "PR/issue closeout failed",
@@ -1997,7 +2756,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
1997
2756
  createdAt: failedAt
1998
2757
  });
1999
2758
  emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
2000
- throw new CliError2(failureDetail, 1);
2759
+ throw new CliError(failureDetail, 1);
2001
2760
  }
2002
2761
  if (verificationAction && !reviewStarted) {
2003
2762
  verificationAction.complete("Completion verification checks finished.");
@@ -2008,10 +2767,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
2008
2767
  if (reviewAction) {
2009
2768
  reviewAction.complete("Run marked completed.");
2010
2769
  }
2011
- patchAuthorityRun(context.projectRoot, input.runId, {
2012
- status: "completed",
2013
- completedAt: new Date().toISOString()
2014
- });
2770
+ setRunStatusOrFail(context.projectRoot, input.runId, "completed", { reason: "driver-completed" });
2015
2771
  emitServerRunEvent({ type: "completed", runId: input.runId });
2016
2772
  await context.emitEvent("command.finished", {
2017
2773
  command: [