@h-rig/cli 0.0.6-alpha.3 → 0.0.6-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig.js +3606 -1172
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +1 -3
- package/dist/src/commands/_doctor-checks.js +13 -27
- package/dist/src/commands/_help-catalog.js +388 -0
- package/dist/src/commands/_operator-surface.js +204 -0
- package/dist/src/commands/_operator-view.js +861 -56
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +841 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-worker-bridge-extension.js +759 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +32 -109
- package/dist/src/commands/_run-driver-helpers.js +0 -2
- package/dist/src/commands/_server-client.js +161 -31
- package/dist/src/commands/_snapshot-upload.js +8 -23
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +9 -9
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +132 -25
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +13 -27
- package/dist/src/commands/github.js +10 -25
- package/dist/src/commands/inbox.js +351 -31
- package/dist/src/commands/init.js +298 -71
- package/dist/src/commands/inspect.js +10 -12
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/plugin.js +76 -22
- package/dist/src/commands/profile-and-review.js +8 -10
- package/dist/src/commands/queue.js +2 -3
- package/dist/src/commands/remote.js +18 -20
- package/dist/src/commands/repo-git-harness.js +6 -8
- package/dist/src/commands/run.js +1157 -122
- package/dist/src/commands/server.js +217 -33
- package/dist/src/commands/setup.js +17 -37
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +660 -73
- package/dist/src/commands/task.js +1542 -252
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +3599 -1159
- package/dist/src/index.js +3646 -1215
- package/dist/src/launcher.js +5 -3
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.js +5 -19
- package/package.json +6 -4
|
@@ -1,7 +1,7 @@
|
|
|
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
|
|
|
@@ -9,8 +9,6 @@ import { createInterface as createLineInterface } from "readline";
|
|
|
9
9
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
10
10
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
11
11
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
12
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
13
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
14
12
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
15
13
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
16
14
|
function formatCommand(parts) {
|
|
@@ -39,10 +37,9 @@ import {
|
|
|
39
37
|
isCodexExecRecord
|
|
40
38
|
} from "@rig/runtime/control-plane/provider/codex-exec-records";
|
|
41
39
|
import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
|
|
42
|
-
import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
40
|
+
import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
43
41
|
import {
|
|
44
|
-
buildTaskRunLifecycleComment
|
|
45
|
-
updateConfiguredTaskSourceTask
|
|
42
|
+
buildTaskRunLifecycleComment
|
|
46
43
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
47
44
|
import {
|
|
48
45
|
closeIssueAfterMergedPr,
|
|
@@ -135,6 +132,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
135
132
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
136
133
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
137
134
|
const next = {
|
|
135
|
+
...existing ?? {},
|
|
138
136
|
runId: input.runId,
|
|
139
137
|
projectRoot,
|
|
140
138
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -387,6 +385,194 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
387
385
|
`);
|
|
388
386
|
}
|
|
389
387
|
|
|
388
|
+
// packages/cli/src/commands/_server-client.ts
|
|
389
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
390
|
+
import { resolve as resolve5 } from "path";
|
|
391
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
392
|
+
|
|
393
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
394
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
395
|
+
import { homedir } from "os";
|
|
396
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
397
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
398
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
399
|
+
if (explicit)
|
|
400
|
+
return resolve4(explicit);
|
|
401
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
402
|
+
if (stateDir)
|
|
403
|
+
return resolve4(stateDir, "connections.json");
|
|
404
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
405
|
+
}
|
|
406
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
407
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
408
|
+
}
|
|
409
|
+
function readJsonFile(path) {
|
|
410
|
+
if (!existsSync2(path))
|
|
411
|
+
return null;
|
|
412
|
+
try {
|
|
413
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
414
|
+
} catch (error) {
|
|
415
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function normalizeConnection(value) {
|
|
419
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
420
|
+
return null;
|
|
421
|
+
const record = value;
|
|
422
|
+
if (record.kind === "local")
|
|
423
|
+
return { kind: "local", mode: "auto" };
|
|
424
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
425
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
426
|
+
return { kind: "remote", baseUrl };
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
function readGlobalConnections(options = {}) {
|
|
431
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
432
|
+
const payload = readJsonFile(path);
|
|
433
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
434
|
+
return { connections: {} };
|
|
435
|
+
}
|
|
436
|
+
const rawConnections = payload.connections;
|
|
437
|
+
const connections = {};
|
|
438
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
439
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
440
|
+
const connection = normalizeConnection(raw);
|
|
441
|
+
if (connection)
|
|
442
|
+
connections[alias] = connection;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return { connections };
|
|
446
|
+
}
|
|
447
|
+
function readRepoConnection(projectRoot) {
|
|
448
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
449
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
450
|
+
return null;
|
|
451
|
+
const record = payload;
|
|
452
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
453
|
+
if (!selected)
|
|
454
|
+
return null;
|
|
455
|
+
return {
|
|
456
|
+
selected,
|
|
457
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
458
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
462
|
+
const repo = readRepoConnection(projectRoot);
|
|
463
|
+
if (!repo)
|
|
464
|
+
return null;
|
|
465
|
+
if (repo.selected === "local")
|
|
466
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
467
|
+
const global = readGlobalConnections(options);
|
|
468
|
+
const connection = global.connections[repo.selected];
|
|
469
|
+
if (!connection) {
|
|
470
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
471
|
+
}
|
|
472
|
+
return { alias: repo.selected, connection };
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// packages/cli/src/commands/_server-client.ts
|
|
476
|
+
var scopedGitHubBearerTokens = new Map;
|
|
477
|
+
function cleanToken(value) {
|
|
478
|
+
const trimmed = value?.trim();
|
|
479
|
+
return trimmed ? trimmed : null;
|
|
480
|
+
}
|
|
481
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
482
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
483
|
+
if (!existsSync3(path))
|
|
484
|
+
return null;
|
|
485
|
+
try {
|
|
486
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
487
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
488
|
+
} catch {
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
493
|
+
const scopedKey = resolve5(projectRoot);
|
|
494
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
495
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
496
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
497
|
+
if (privateSession)
|
|
498
|
+
return privateSession;
|
|
499
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
500
|
+
}
|
|
501
|
+
async function ensureServerForCli(projectRoot) {
|
|
502
|
+
try {
|
|
503
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
504
|
+
if (selected?.connection.kind === "remote") {
|
|
505
|
+
return {
|
|
506
|
+
baseUrl: selected.connection.baseUrl,
|
|
507
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
508
|
+
connectionKind: "remote"
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
512
|
+
return {
|
|
513
|
+
baseUrl: connection.baseUrl,
|
|
514
|
+
authToken: connection.authToken,
|
|
515
|
+
connectionKind: "local"
|
|
516
|
+
};
|
|
517
|
+
} catch (error) {
|
|
518
|
+
if (error instanceof Error) {
|
|
519
|
+
throw new CliError2(error.message, 1);
|
|
520
|
+
}
|
|
521
|
+
throw error;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function mergeHeaders(headers, authToken) {
|
|
525
|
+
const merged = new Headers(headers);
|
|
526
|
+
if (authToken) {
|
|
527
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
528
|
+
}
|
|
529
|
+
return merged;
|
|
530
|
+
}
|
|
531
|
+
function diagnosticMessage(payload) {
|
|
532
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
533
|
+
return null;
|
|
534
|
+
const record = payload;
|
|
535
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
536
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
537
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
538
|
+
return [];
|
|
539
|
+
const diagnostic = entry;
|
|
540
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
541
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
542
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
543
|
+
});
|
|
544
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
545
|
+
}
|
|
546
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
547
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
548
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
549
|
+
...init,
|
|
550
|
+
headers: mergeHeaders(init.headers, server.authToken)
|
|
551
|
+
});
|
|
552
|
+
const text = await response.text();
|
|
553
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
554
|
+
try {
|
|
555
|
+
return JSON.parse(text);
|
|
556
|
+
} catch {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
})() : null;
|
|
560
|
+
if (!response.ok) {
|
|
561
|
+
const diagnostics = diagnosticMessage(payload);
|
|
562
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
563
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
564
|
+
}
|
|
565
|
+
return payload;
|
|
566
|
+
}
|
|
567
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
568
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "content-type": "application/json" },
|
|
571
|
+
body: JSON.stringify(input)
|
|
572
|
+
});
|
|
573
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
574
|
+
}
|
|
575
|
+
|
|
390
576
|
// packages/cli/src/commands/task-run-driver.ts
|
|
391
577
|
var PI_CANONICAL_RUN_STAGES = [
|
|
392
578
|
"Connect",
|
|
@@ -430,6 +616,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
430
616
|
RIG_SERVER_RUN_ID: input.runId,
|
|
431
617
|
RIG_TASK_ID: input.taskId,
|
|
432
618
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
619
|
+
RIG_STEERING_POLL_MS: "0",
|
|
433
620
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
434
621
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
435
622
|
...githubBridgeEnv(githubToken)
|
|
@@ -454,12 +641,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
454
641
|
return 0;
|
|
455
642
|
let copied = 0;
|
|
456
643
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
457
|
-
const sourcePath =
|
|
458
|
-
const targetPath =
|
|
644
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
645
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
459
646
|
try {
|
|
460
647
|
if (!statSync(sourcePath).isFile())
|
|
461
648
|
continue;
|
|
462
|
-
|
|
649
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
463
650
|
copyFileSync(sourcePath, targetPath);
|
|
464
651
|
copied += 1;
|
|
465
652
|
} catch {}
|
|
@@ -498,7 +685,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
498
685
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
499
686
|
return {
|
|
500
687
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
501
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
688
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
502
689
|
};
|
|
503
690
|
}
|
|
504
691
|
function positiveInt(value, fallback) {
|
|
@@ -506,7 +693,7 @@ function positiveInt(value, fallback) {
|
|
|
506
693
|
}
|
|
507
694
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
508
695
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
509
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
696
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
510
697
|
return {
|
|
511
698
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
512
699
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -616,9 +803,9 @@ function createCommandRunner(binary) {
|
|
|
616
803
|
const stderrChunks = [];
|
|
617
804
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
618
805
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
|
-
return await new Promise((
|
|
620
|
-
child.once("error", (error) =>
|
|
621
|
-
child.once("close", (code) =>
|
|
806
|
+
return await new Promise((resolve7) => {
|
|
807
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
808
|
+
child.once("close", (code) => resolve7({
|
|
622
809
|
exitCode: code ?? 1,
|
|
623
810
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
624
811
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -642,8 +829,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
642
829
|
if (!taskId) {
|
|
643
830
|
return { status: "skipped" };
|
|
644
831
|
}
|
|
645
|
-
const
|
|
646
|
-
const
|
|
832
|
+
const configInput = input.config ?? null;
|
|
833
|
+
const config = configInput ?? {};
|
|
834
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
647
835
|
if (prMode === "off" || prMode === "ask") {
|
|
648
836
|
input.appendStage?.("Open PR", prMode === "off" ? "PR automation disabled by pr.mode=off." : "PR creation awaiting operator approval by pr.mode=ask.", "skipped", "info");
|
|
649
837
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -659,7 +847,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
659
847
|
gitCommand
|
|
660
848
|
});
|
|
661
849
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
662
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
850
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
663
851
|
const stage = input.appendStage ?? (() => {
|
|
664
852
|
return;
|
|
665
853
|
});
|
|
@@ -691,7 +879,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
691
879
|
config,
|
|
692
880
|
sourceTask: input.sourceTask,
|
|
693
881
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
882
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
694
883
|
command: ghCommand,
|
|
884
|
+
gitCommand,
|
|
695
885
|
steerPi,
|
|
696
886
|
lifecycle: {
|
|
697
887
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -740,8 +930,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
740
930
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
741
931
|
})
|
|
742
932
|
}
|
|
743
|
-
}).catch(() => {
|
|
744
|
-
return;
|
|
745
933
|
});
|
|
746
934
|
}
|
|
747
935
|
},
|
|
@@ -760,8 +948,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
760
948
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
761
949
|
})
|
|
762
950
|
}
|
|
763
|
-
}).catch(() => {
|
|
764
|
-
return;
|
|
765
951
|
});
|
|
766
952
|
}
|
|
767
953
|
},
|
|
@@ -790,8 +976,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
790
976
|
errorText: detail
|
|
791
977
|
})
|
|
792
978
|
}
|
|
793
|
-
}).catch(() => {
|
|
794
|
-
|
|
979
|
+
}).catch((error) => {
|
|
980
|
+
try {
|
|
981
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
982
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
983
|
+
title: "Task source needs-attention update failed",
|
|
984
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
985
|
+
tone: "error",
|
|
986
|
+
status: "needs_attention",
|
|
987
|
+
createdAt: new Date().toISOString()
|
|
988
|
+
});
|
|
989
|
+
} catch {}
|
|
795
990
|
});
|
|
796
991
|
}
|
|
797
992
|
return { status: "needs_attention", pr };
|
|
@@ -814,7 +1009,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
814
1009
|
return null;
|
|
815
1010
|
}
|
|
816
1011
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
817
|
-
const summary =
|
|
1012
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
818
1013
|
if (!summary || summary.status !== "fail") {
|
|
819
1014
|
continue;
|
|
820
1015
|
}
|
|
@@ -895,14 +1090,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
895
1090
|
if (!input.taskId || !input.workspaceDir) {
|
|
896
1091
|
return { accepted: false, reason: null };
|
|
897
1092
|
}
|
|
898
|
-
const artifactDir =
|
|
899
|
-
const reviewStatusPath =
|
|
900
|
-
const taskResultPath =
|
|
1093
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1094
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1095
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
901
1096
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
902
1097
|
if (reviewStatus !== "APPROVED") {
|
|
903
1098
|
return { accepted: false, reason: null };
|
|
904
1099
|
}
|
|
905
|
-
const taskResult =
|
|
1100
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
906
1101
|
if (taskResult?.status !== "completed") {
|
|
907
1102
|
return { accepted: false, reason: null };
|
|
908
1103
|
}
|
|
@@ -934,13 +1129,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
934
1129
|
if (!input.taskId || !input.workspaceDir) {
|
|
935
1130
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
936
1131
|
}
|
|
937
|
-
const artifactDir =
|
|
938
|
-
const reviewStatePath =
|
|
939
|
-
const reviewFeedbackPath =
|
|
940
|
-
const reviewStatusPath =
|
|
941
|
-
const failedApproachesPath =
|
|
942
|
-
const validationSummaryPath =
|
|
943
|
-
const reviewState =
|
|
1132
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1133
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1134
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1135
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1136
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1137
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1138
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
944
1139
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
945
1140
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
946
1141
|
if (reviewStatus === "APPROVED") {
|
|
@@ -993,12 +1188,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
993
1188
|
}
|
|
994
1189
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
995
1190
|
}
|
|
1191
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1192
|
+
let nextAssistantText = input.assistantText;
|
|
1193
|
+
if (input.record.type === "message_update") {
|
|
1194
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1195
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1196
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1197
|
+
}
|
|
1198
|
+
} else if (input.record.type === "stream_event") {
|
|
1199
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1200
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1201
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1202
|
+
nextAssistantText += delta.text;
|
|
1203
|
+
}
|
|
1204
|
+
} else if (input.record.type === "assistant") {
|
|
1205
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1206
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1207
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1208
|
+
if (fullText.length > nextAssistantText.length)
|
|
1209
|
+
nextAssistantText = fullText;
|
|
1210
|
+
}
|
|
1211
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1212
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1213
|
+
id: input.messageId,
|
|
1214
|
+
type: "assistant_message",
|
|
1215
|
+
text: nextAssistantText,
|
|
1216
|
+
state: "streaming",
|
|
1217
|
+
createdAt: new Date().toISOString()
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
return nextAssistantText;
|
|
1221
|
+
}
|
|
1222
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1223
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1224
|
+
if (type === "response") {
|
|
1225
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1226
|
+
const success = input.record.success !== false;
|
|
1227
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
1230
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1231
|
+
id: input.nextRunLogId(),
|
|
1232
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1233
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1234
|
+
tone: success ? "tool" : "error",
|
|
1235
|
+
status: input.status,
|
|
1236
|
+
payload: input.record,
|
|
1237
|
+
createdAt: new Date().toISOString()
|
|
1238
|
+
});
|
|
1239
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
if (type !== "extension_ui_request")
|
|
1243
|
+
return false;
|
|
1244
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1245
|
+
let title = "Pi UI event";
|
|
1246
|
+
let detail = method;
|
|
1247
|
+
let tone = "info";
|
|
1248
|
+
if (method === "notify") {
|
|
1249
|
+
title = "Pi notification";
|
|
1250
|
+
detail = String(input.record.message ?? "");
|
|
1251
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1252
|
+
} else if (method === "setStatus") {
|
|
1253
|
+
title = "Pi UI status";
|
|
1254
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1255
|
+
tone = "tool";
|
|
1256
|
+
} else if (method === "setWidget") {
|
|
1257
|
+
title = "Pi UI widget";
|
|
1258
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1259
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1260
|
+
tone = "tool";
|
|
1261
|
+
} else if (method === "setTitle") {
|
|
1262
|
+
title = "Pi UI title";
|
|
1263
|
+
detail = String(input.record.title ?? "");
|
|
1264
|
+
tone = "tool";
|
|
1265
|
+
} else if (method === "set_editor_text") {
|
|
1266
|
+
title = "Pi editor update";
|
|
1267
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1268
|
+
tone = "tool";
|
|
1269
|
+
} else {
|
|
1270
|
+
title = "Pi UI request";
|
|
1271
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1272
|
+
}
|
|
1273
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1274
|
+
id: input.nextRunLogId(),
|
|
1275
|
+
title,
|
|
1276
|
+
detail,
|
|
1277
|
+
tone,
|
|
1278
|
+
status: input.status,
|
|
1279
|
+
payload: input.record,
|
|
1280
|
+
createdAt: new Date().toISOString()
|
|
1281
|
+
});
|
|
1282
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1283
|
+
return true;
|
|
1284
|
+
}
|
|
1285
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1286
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1287
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1288
|
+
return false;
|
|
1289
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1290
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1291
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1292
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1293
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1294
|
+
type,
|
|
1295
|
+
toolName,
|
|
1296
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1297
|
+
createdAt: new Date().toISOString()
|
|
1298
|
+
});
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1302
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1303
|
+
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");
|
|
1304
|
+
}
|
|
1305
|
+
function appendToolTimelineFromLog(input) {
|
|
1306
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1307
|
+
if (title !== "Tool activity")
|
|
1308
|
+
return;
|
|
1309
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1310
|
+
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;
|
|
1311
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1312
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1313
|
+
id: `tool:${logId}`,
|
|
1314
|
+
type: "tool_execution_update",
|
|
1315
|
+
toolName,
|
|
1316
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1317
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1318
|
+
payload,
|
|
1319
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
996
1322
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
997
|
-
if (!
|
|
1323
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
998
1324
|
return null;
|
|
999
1325
|
}
|
|
1000
1326
|
try {
|
|
1001
|
-
const status =
|
|
1327
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1002
1328
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1003
1329
|
} catch {
|
|
1004
1330
|
return null;
|
|
@@ -1016,7 +1342,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1016
1342
|
function runSourceTaskIdentity(sourceTask) {
|
|
1017
1343
|
return sourceTask;
|
|
1018
1344
|
}
|
|
1019
|
-
|
|
1345
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1346
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1347
|
+
return null;
|
|
1348
|
+
const record = sourceTask;
|
|
1349
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1350
|
+
if (direct?.trim())
|
|
1351
|
+
return direct.trim();
|
|
1352
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1353
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1354
|
+
}
|
|
1355
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1356
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1357
|
+
id: input.taskId,
|
|
1358
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1359
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1360
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1361
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1362
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1363
|
+
});
|
|
1364
|
+
if (serverResult.ok === false) {
|
|
1365
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1366
|
+
}
|
|
1367
|
+
return {
|
|
1368
|
+
updated: serverResult.ok !== false,
|
|
1369
|
+
taskId: input.taskId,
|
|
1370
|
+
status: input.update.status,
|
|
1371
|
+
source: "server",
|
|
1372
|
+
sourceKind: "server",
|
|
1373
|
+
projectSync: serverResult.projectSync
|
|
1374
|
+
};
|
|
1375
|
+
};
|
|
1376
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1020
1377
|
if (!taskId)
|
|
1021
1378
|
return;
|
|
1022
1379
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1081,6 +1438,9 @@ function stringArrayField(record, key) {
|
|
|
1081
1438
|
}
|
|
1082
1439
|
async function executeRigOwnedTaskRun(context, input) {
|
|
1083
1440
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
1441
|
+
const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
|
|
1442
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
1443
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
1084
1444
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
1085
1445
|
let prompt = buildRunPrompt({
|
|
1086
1446
|
projectRoot: context.projectRoot,
|
|
@@ -1103,11 +1463,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1103
1463
|
...input.model ? ["--model", input.model] : [],
|
|
1104
1464
|
"--prompt"
|
|
1105
1465
|
] : input.runtimeAdapter === "pi" ? [
|
|
1106
|
-
"
|
|
1107
|
-
"--verbose",
|
|
1108
|
-
"--mode",
|
|
1109
|
-
"json",
|
|
1110
|
-
"--no-session",
|
|
1466
|
+
"__rig_pi_session_daemon__",
|
|
1111
1467
|
...input.model ? ["--model", input.model] : []
|
|
1112
1468
|
] : [
|
|
1113
1469
|
"--print",
|
|
@@ -1136,14 +1492,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1136
1492
|
taskId: runtimeTaskId,
|
|
1137
1493
|
createdAt: startedAt,
|
|
1138
1494
|
runtimeAdapter: input.runtimeAdapter,
|
|
1139
|
-
status: "created"
|
|
1495
|
+
status: resumeMode ? "preparing" : "created"
|
|
1140
1496
|
});
|
|
1141
1497
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1142
1498
|
status: "preparing",
|
|
1143
1499
|
startedAt,
|
|
1144
1500
|
completedAt: null,
|
|
1145
1501
|
errorText: null,
|
|
1146
|
-
artifactRoot: null,
|
|
1502
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
1147
1503
|
runtimeAdapter: input.runtimeAdapter,
|
|
1148
1504
|
runtimeMode: input.runtimeMode,
|
|
1149
1505
|
interactionMode: input.interactionMode,
|
|
@@ -1159,9 +1515,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1159
1515
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
1160
1516
|
});
|
|
1161
1517
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1162
|
-
id: `log:${input.runId}:start`,
|
|
1163
|
-
title: "Rig task run started",
|
|
1164
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
1518
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
1519
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
1520
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
1165
1521
|
tone: "info",
|
|
1166
1522
|
status: "preparing",
|
|
1167
1523
|
createdAt: startedAt
|
|
@@ -1182,15 +1538,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1182
1538
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1183
1539
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1184
1540
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1185
|
-
const planningArtifactPath =
|
|
1541
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1186
1542
|
const persistedPlanning = {
|
|
1187
1543
|
...planningClassification,
|
|
1188
1544
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1189
1545
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1190
1546
|
classifiedAt: new Date().toISOString()
|
|
1191
1547
|
};
|
|
1192
|
-
|
|
1193
|
-
|
|
1548
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1549
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1194
1550
|
`, "utf8");
|
|
1195
1551
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1196
1552
|
prompt = `${prompt}
|
|
@@ -1204,7 +1560,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1204
1560
|
projectRoot: context.projectRoot,
|
|
1205
1561
|
runId: input.runId,
|
|
1206
1562
|
stage,
|
|
1207
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1563
|
+
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,
|
|
1208
1564
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1209
1565
|
});
|
|
1210
1566
|
}
|
|
@@ -1239,12 +1595,13 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1239
1595
|
let reviewAction;
|
|
1240
1596
|
let verificationStarted = false;
|
|
1241
1597
|
let reviewStarted = false;
|
|
1242
|
-
let latestRuntimeWorkspace = null;
|
|
1243
|
-
let latestSessionDir = null;
|
|
1244
|
-
let latestLogsDir = null;
|
|
1598
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1599
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1600
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1245
1601
|
let latestProviderCommand = null;
|
|
1246
|
-
let latestRuntimeBranch = null;
|
|
1602
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1247
1603
|
let snapshotSidecarPromise = null;
|
|
1604
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1248
1605
|
let dirtyBaselineApplied = false;
|
|
1249
1606
|
const childEnv = {
|
|
1250
1607
|
...process.env,
|
|
@@ -1261,7 +1618,11 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1261
1618
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
1262
1619
|
RIG_SERVER_RUN_ID: input.runId
|
|
1263
1620
|
},
|
|
1264
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
1621
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
1622
|
+
...resumeMode ? {
|
|
1623
|
+
RIG_RUN_RESUME: "1",
|
|
1624
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
1625
|
+
} : {}
|
|
1265
1626
|
};
|
|
1266
1627
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
1267
1628
|
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
@@ -1293,6 +1654,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1293
1654
|
detail: detail ?? "Verifier review is running."
|
|
1294
1655
|
});
|
|
1295
1656
|
};
|
|
1657
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1296
1658
|
const handleWrapperEvent = (rawPayload) => {
|
|
1297
1659
|
let event = null;
|
|
1298
1660
|
try {
|
|
@@ -1308,6 +1670,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1308
1670
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1309
1671
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1310
1672
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1673
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1311
1674
|
latestRuntimeBranch = runtimeId;
|
|
1312
1675
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1313
1676
|
runtimeId: runtimeId ?? null,
|
|
@@ -1319,10 +1682,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1319
1682
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1320
1683
|
status: "running",
|
|
1321
1684
|
worktreePath: latestRuntimeWorkspace,
|
|
1322
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1685
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1323
1686
|
logRoot: latestLogsDir,
|
|
1324
|
-
sessionPath: latestSessionDir ?
|
|
1325
|
-
sessionLogPath: latestLogsDir ?
|
|
1687
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1688
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1326
1689
|
branch: runtimeId
|
|
1327
1690
|
});
|
|
1328
1691
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1330,8 +1693,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1330
1693
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1331
1694
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1332
1695
|
if (readyFile) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1696
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1697
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1335
1698
|
`, "utf8");
|
|
1336
1699
|
}
|
|
1337
1700
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1345,7 +1708,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1345
1708
|
});
|
|
1346
1709
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1347
1710
|
}
|
|
1348
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1711
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1349
1712
|
snapshotSidecarPromise = (async () => {
|
|
1350
1713
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1351
1714
|
projectRoot: context.projectRoot,
|
|
@@ -1427,9 +1790,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1427
1790
|
}
|
|
1428
1791
|
return true;
|
|
1429
1792
|
}
|
|
1793
|
+
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") {
|
|
1794
|
+
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";
|
|
1795
|
+
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");
|
|
1796
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1797
|
+
id: nextRunLogId(),
|
|
1798
|
+
title,
|
|
1799
|
+
detail,
|
|
1800
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1801
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1802
|
+
payload: {
|
|
1803
|
+
eventType: event.type,
|
|
1804
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1805
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1806
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1807
|
+
},
|
|
1808
|
+
createdAt: new Date().toISOString()
|
|
1809
|
+
});
|
|
1810
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1811
|
+
return true;
|
|
1812
|
+
}
|
|
1813
|
+
if (event.type === "pi.session.ready") {
|
|
1814
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1815
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1816
|
+
if (privateMetadata) {
|
|
1817
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1818
|
+
piSession: publicMetadata,
|
|
1819
|
+
piSessionPrivate: privateMetadata
|
|
1820
|
+
});
|
|
1821
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1822
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1823
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1824
|
+
title: "Worker Pi session ready",
|
|
1825
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1826
|
+
tone: "info",
|
|
1827
|
+
status: "running",
|
|
1828
|
+
payload: {
|
|
1829
|
+
sessionId: sessionId || null,
|
|
1830
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1831
|
+
},
|
|
1832
|
+
createdAt: new Date().toISOString()
|
|
1833
|
+
});
|
|
1834
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1835
|
+
}
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1838
|
+
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") {
|
|
1839
|
+
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";
|
|
1840
|
+
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")}`;
|
|
1841
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1842
|
+
id: nextRunLogId(),
|
|
1843
|
+
title,
|
|
1844
|
+
detail,
|
|
1845
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1846
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1847
|
+
payload,
|
|
1848
|
+
createdAt: new Date().toISOString()
|
|
1849
|
+
});
|
|
1850
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1851
|
+
return true;
|
|
1852
|
+
}
|
|
1430
1853
|
return false;
|
|
1431
1854
|
};
|
|
1432
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1433
1855
|
const handleAgentStdoutLine = (line) => {
|
|
1434
1856
|
const trimmed = line.trim();
|
|
1435
1857
|
if (!trimmed)
|
|
@@ -1463,6 +1885,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1463
1885
|
try {
|
|
1464
1886
|
const record = JSON.parse(trimmed);
|
|
1465
1887
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1888
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1889
|
+
projectRoot: context.projectRoot,
|
|
1890
|
+
runId: input.runId,
|
|
1891
|
+
record,
|
|
1892
|
+
status: liveLogStatus,
|
|
1893
|
+
nextRunLogId
|
|
1894
|
+
})) {
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1898
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1466
1901
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1467
1902
|
runId: input.runId,
|
|
1468
1903
|
record,
|
|
@@ -1479,7 +1914,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1479
1914
|
if (providerLogs.length > 0) {
|
|
1480
1915
|
for (const providerLog of providerLogs) {
|
|
1481
1916
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1917
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1482
1918
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1919
|
+
if (providerLog.title === "Tool activity")
|
|
1920
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1483
1921
|
}
|
|
1484
1922
|
}
|
|
1485
1923
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1531,6 +1969,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1531
1969
|
return;
|
|
1532
1970
|
}
|
|
1533
1971
|
}
|
|
1972
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1534
1975
|
if (record.type === "assistant") {
|
|
1535
1976
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1536
1977
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1567,7 +2008,36 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1567
2008
|
let reviewFailureDetail = null;
|
|
1568
2009
|
const stderrLines = [];
|
|
1569
2010
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
1570
|
-
|
|
2011
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
2012
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2013
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
2014
|
+
title: "Resume continuing from closeout phase",
|
|
2015
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
2016
|
+
tone: "info",
|
|
2017
|
+
status: resumePreviousStatus,
|
|
2018
|
+
createdAt: new Date().toISOString()
|
|
2019
|
+
});
|
|
2020
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
2021
|
+
exit = { code: 0, signal: null };
|
|
2022
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
2023
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
2024
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
2025
|
+
workspaceDir: latestRuntimeWorkspace
|
|
2026
|
+
});
|
|
2027
|
+
if (acceptedArtifactState.accepted) {
|
|
2028
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2029
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
2030
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
2031
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
2032
|
+
tone: "info",
|
|
2033
|
+
status: "validating",
|
|
2034
|
+
createdAt: new Date().toISOString()
|
|
2035
|
+
});
|
|
2036
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
2037
|
+
exit = { code: 0, signal: null };
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
1571
2041
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
1572
2042
|
const child = spawn(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
1573
2043
|
cwd: context.projectRoot,
|
|
@@ -1608,7 +2078,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1608
2078
|
let acceptedArtifactObservedAt = null;
|
|
1609
2079
|
let acceptedArtifactPollTimer = null;
|
|
1610
2080
|
let acceptedArtifactKillTimer = null;
|
|
1611
|
-
const attemptExit = await new Promise((
|
|
2081
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1612
2082
|
let settled = false;
|
|
1613
2083
|
const settle = (result) => {
|
|
1614
2084
|
if (settled)
|
|
@@ -1616,7 +2086,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1616
2086
|
settled = true;
|
|
1617
2087
|
if (acceptedArtifactPollTimer)
|
|
1618
2088
|
clearInterval(acceptedArtifactPollTimer);
|
|
1619
|
-
|
|
2089
|
+
resolve7(result);
|
|
1620
2090
|
};
|
|
1621
2091
|
const pollAcceptedArtifacts = () => {
|
|
1622
2092
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1689,7 +2159,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1689
2159
|
});
|
|
1690
2160
|
for (const pendingLog of pendingLogs) {
|
|
1691
2161
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2162
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1692
2163
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2164
|
+
if (pendingLog.title === "Tool activity")
|
|
2165
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1693
2166
|
}
|
|
1694
2167
|
process.off("SIGTERM", forwardSigterm);
|
|
1695
2168
|
if (attemptExit.error) {
|
|
@@ -1815,8 +2288,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1815
2288
|
}
|
|
1816
2289
|
if (planningClassification.planningRequired) {
|
|
1817
2290
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1818
|
-
const expectedPlanPath =
|
|
1819
|
-
if (!
|
|
2291
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2292
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1820
2293
|
const failedAt = new Date().toISOString();
|
|
1821
2294
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1822
2295
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1836,6 +2309,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1836
2309
|
throw new CliError2(failureDetail, 1);
|
|
1837
2310
|
}
|
|
1838
2311
|
}
|
|
2312
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2313
|
+
appendPiStageLog({
|
|
2314
|
+
projectRoot: context.projectRoot,
|
|
2315
|
+
runId: input.runId,
|
|
2316
|
+
stage: "Validate",
|
|
2317
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2318
|
+
status: "completed"
|
|
2319
|
+
});
|
|
2320
|
+
if (verificationAction && !reviewStarted) {
|
|
2321
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2322
|
+
}
|
|
2323
|
+
if (!reviewAction) {
|
|
2324
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2325
|
+
}
|
|
2326
|
+
if (reviewAction) {
|
|
2327
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2328
|
+
}
|
|
2329
|
+
const requestedAt = new Date().toISOString();
|
|
2330
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2331
|
+
status: "reviewing",
|
|
2332
|
+
completedAt: null,
|
|
2333
|
+
errorText: null,
|
|
2334
|
+
serverCloseout: {
|
|
2335
|
+
status: "pending",
|
|
2336
|
+
phase: "queued",
|
|
2337
|
+
requestedAt,
|
|
2338
|
+
updatedAt: requestedAt,
|
|
2339
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2340
|
+
branch: latestRuntimeBranch,
|
|
2341
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2342
|
+
}
|
|
2343
|
+
});
|
|
2344
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2345
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2346
|
+
title: "Server-owned closeout requested",
|
|
2347
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2348
|
+
tone: "info",
|
|
2349
|
+
status: "reviewing",
|
|
2350
|
+
createdAt: requestedAt,
|
|
2351
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2352
|
+
});
|
|
2353
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2354
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2355
|
+
await context.emitEvent("command.finished", {
|
|
2356
|
+
command: [
|
|
2357
|
+
"rig",
|
|
2358
|
+
"server",
|
|
2359
|
+
"task-run",
|
|
2360
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2361
|
+
...input.title ? ["--title", input.title] : []
|
|
2362
|
+
],
|
|
2363
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2364
|
+
exitCode: 0,
|
|
2365
|
+
durationMs: 0,
|
|
2366
|
+
startedAt,
|
|
2367
|
+
finishedAt: requestedAt
|
|
2368
|
+
});
|
|
2369
|
+
process.exit(0);
|
|
2370
|
+
}
|
|
1839
2371
|
const runPiPrFeedbackFix = async (message) => {
|
|
1840
2372
|
appendPiStageLog({
|
|
1841
2373
|
projectRoot: context.projectRoot,
|
|
@@ -1863,11 +2395,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1863
2395
|
child.stdin.write(message);
|
|
1864
2396
|
}
|
|
1865
2397
|
child.stdin.end();
|
|
2398
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2399
|
+
let feedbackAssistantText = "";
|
|
2400
|
+
const feedbackPendingToolUses = new Map;
|
|
1866
2401
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1867
2402
|
stdout.on("line", (line) => {
|
|
1868
2403
|
const trimmed = line.trim();
|
|
1869
2404
|
if (!trimmed)
|
|
1870
2405
|
return;
|
|
2406
|
+
try {
|
|
2407
|
+
const record = JSON.parse(trimmed);
|
|
2408
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2409
|
+
runId: input.runId,
|
|
2410
|
+
record,
|
|
2411
|
+
createdAtFallback: new Date().toISOString(),
|
|
2412
|
+
status: "reviewing",
|
|
2413
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2414
|
+
});
|
|
2415
|
+
for (const providerLog of providerLogs) {
|
|
2416
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2417
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2418
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2419
|
+
if (providerLog.title === "Tool activity")
|
|
2420
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2421
|
+
}
|
|
2422
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2423
|
+
projectRoot: context.projectRoot,
|
|
2424
|
+
runId: input.runId,
|
|
2425
|
+
messageId: feedbackAssistantMessageId,
|
|
2426
|
+
record,
|
|
2427
|
+
assistantText: feedbackAssistantText
|
|
2428
|
+
});
|
|
2429
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2430
|
+
if (hadAssistantDelta) {
|
|
2431
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2432
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2433
|
+
}
|
|
2434
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2435
|
+
return;
|
|
2436
|
+
} catch {}
|
|
1871
2437
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1872
2438
|
id: nextRunLogId(),
|
|
1873
2439
|
title: "Pi PR feedback fix output",
|
|
@@ -1893,10 +2459,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1893
2459
|
});
|
|
1894
2460
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1895
2461
|
});
|
|
1896
|
-
const exitCode = await new Promise((
|
|
1897
|
-
child.once("error", () =>
|
|
1898
|
-
child.once("close", (code) =>
|
|
2462
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2463
|
+
child.once("error", () => resolve7(1));
|
|
2464
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1899
2465
|
});
|
|
2466
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2467
|
+
runId: input.runId,
|
|
2468
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2469
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2470
|
+
})) {
|
|
2471
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2472
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2473
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2474
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2475
|
+
}
|
|
2476
|
+
if (feedbackAssistantText.trim()) {
|
|
2477
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2478
|
+
id: feedbackAssistantMessageId,
|
|
2479
|
+
type: "assistant_message",
|
|
2480
|
+
text: feedbackAssistantText,
|
|
2481
|
+
state: "completed",
|
|
2482
|
+
createdAt: new Date().toISOString(),
|
|
2483
|
+
completedAt: new Date().toISOString()
|
|
2484
|
+
});
|
|
2485
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2486
|
+
}
|
|
1900
2487
|
if (exitCode !== 0) {
|
|
1901
2488
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1902
2489
|
}
|