@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.
- package/README.md +1 -1
- package/dist/bin/rig.js +4507 -1506
- package/dist/src/commands/_async-ui.js +152 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.js +177 -43
- package/dist/src/commands/_help-catalog.js +485 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.js +595 -72
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_pi-frontend.js +411 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.js +187 -127
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.js +343 -60
- package/dist/src/commands/_snapshot-upload.js +160 -38
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.js +28 -21
- package/dist/src/commands/connect.js +146 -33
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.js +304 -44
- package/dist/src/commands/github.js +301 -52
- package/dist/src/commands/inbox.js +679 -72
- package/dist/src/commands/init.js +622 -118
- package/dist/src/commands/inspect.js +515 -32
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.js +1162 -158
- package/dist/src/commands/server.js +373 -56
- package/dist/src/commands/setup.js +316 -62
- package/dist/src/commands/stats.js +1030 -0
- package/dist/src/commands/task-report-bug.js +29 -22
- package/dist/src/commands/task-run-driver.js +862 -129
- package/dist/src/commands/task.js +1423 -311
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.js +4446 -1499
- package/dist/src/index.js +4502 -1504
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.js +16 -22
- 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
|
|
4
|
-
import { resolve as
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
appendRunJournalEvent,
|
|
65
|
+
appendRunLogEntry,
|
|
66
|
+
appendRunTimelineEntry,
|
|
67
|
+
patchAuthorityRunRecord,
|
|
58
68
|
readAuthorityRun as readAuthorityRun2,
|
|
59
|
-
resolveAuthorityRunDir
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
178
|
-
if (!
|
|
179
|
-
throw new
|
|
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
|
-
|
|
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
|
-
|
|
215
|
+
appendRunTimelineEntry(projectRoot, runId, value);
|
|
201
216
|
touchAuthorityRun(projectRoot, runId);
|
|
202
217
|
}
|
|
203
218
|
function appendRunLog(projectRoot, runId, value) {
|
|
204
|
-
|
|
219
|
+
appendRunLogEntry(projectRoot, runId, value);
|
|
205
220
|
touchAuthorityRun(projectRoot, runId);
|
|
206
221
|
}
|
|
207
222
|
function appendRunAction(projectRoot, runId, value) {
|
|
208
|
-
|
|
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 =
|
|
459
|
-
const targetPath =
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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((
|
|
621
|
-
child.once("error", (error) =>
|
|
622
|
-
child.once("close", (code) =>
|
|
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
|
|
647
|
-
const
|
|
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 ??
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
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 =
|
|
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 =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
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 (!
|
|
1499
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1500
|
return null;
|
|
1000
1501
|
}
|
|
1001
1502
|
try {
|
|
1002
|
-
const status =
|
|
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
|
-
|
|
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
|
-
"
|
|
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 =
|
|
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
|
-
|
|
1197
|
-
|
|
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
|
|
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" ?
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
1867
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1331
1868
|
logRoot: latestLogsDir,
|
|
1332
|
-
sessionPath: latestSessionDir ?
|
|
1333
|
-
sessionLogPath: latestLogsDir ?
|
|
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
|
-
|
|
1342
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 ? "
|
|
2444
|
+
const terminalRunStatus = reviewFailureDetail ? "needs-attention" : "failed";
|
|
1822
2445
|
try {
|
|
1823
|
-
await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "
|
|
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
|
-
|
|
1834
|
-
|
|
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 === "
|
|
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
|
|
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 =
|
|
1856
|
-
if (!
|
|
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
|
-
|
|
1860
|
-
|
|
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
|
|
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((
|
|
1934
|
-
child.once("error", () =>
|
|
1935
|
-
child.once("close", (code) =>
|
|
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
|
-
|
|
1973
|
-
|
|
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
|
|
2714
|
+
throw new CliError(failureDetail, 1);
|
|
1979
2715
|
}
|
|
1980
2716
|
} catch (error) {
|
|
1981
|
-
if (error instanceof
|
|
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
|
-
|
|
1987
|
-
|
|
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
|
|
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
|
-
|
|
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: [
|