@h-rig/cli 0.0.6-alpha.13 → 0.0.6-alpha.14
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 +710 -226
- package/dist/src/commands/_doctor-checks.js +7 -20
- package/dist/src/commands/_operator-surface.js +157 -0
- package/dist/src/commands/_operator-view.js +160 -51
- package/dist/src/commands/_preflight.js +30 -26
- package/dist/src/commands/_server-client.js +46 -22
- package/dist/src/commands/_snapshot-upload.js +7 -20
- package/dist/src/commands/_task-picker.js +21 -13
- package/dist/src/commands/agent.js +1 -0
- package/dist/src/commands/doctor.js +7 -20
- package/dist/src/commands/github.js +9 -22
- package/dist/src/commands/init.js +183 -44
- package/dist/src/commands/queue.js +1 -0
- package/dist/src/commands/run.js +172 -76
- package/dist/src/commands/server.js +7 -20
- package/dist/src/commands/setup.js +7 -20
- package/dist/src/commands/task-run-driver.js +446 -53
- package/dist/src/commands/task.js +231 -98
- package/dist/src/commands.js +702 -218
- package/dist/src/index.js +710 -226
- package/package.json +5 -5
|
@@ -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
|
|
|
@@ -39,10 +39,9 @@ import {
|
|
|
39
39
|
isCodexExecRecord
|
|
40
40
|
} from "@rig/runtime/control-plane/provider/codex-exec-records";
|
|
41
41
|
import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
|
|
42
|
-
import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
42
|
+
import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
43
43
|
import {
|
|
44
|
-
buildTaskRunLifecycleComment
|
|
45
|
-
updateConfiguredTaskSourceTask
|
|
44
|
+
buildTaskRunLifecycleComment
|
|
46
45
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
47
46
|
import {
|
|
48
47
|
closeIssueAfterMergedPr,
|
|
@@ -388,6 +387,194 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
388
387
|
`);
|
|
389
388
|
}
|
|
390
389
|
|
|
390
|
+
// packages/cli/src/commands/_server-client.ts
|
|
391
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
392
|
+
import { resolve as resolve5 } from "path";
|
|
393
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
394
|
+
|
|
395
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
396
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
397
|
+
import { homedir } from "os";
|
|
398
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
399
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
400
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
401
|
+
if (explicit)
|
|
402
|
+
return resolve4(explicit);
|
|
403
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
404
|
+
if (stateDir)
|
|
405
|
+
return resolve4(stateDir, "connections.json");
|
|
406
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
407
|
+
}
|
|
408
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
409
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
410
|
+
}
|
|
411
|
+
function readJsonFile(path) {
|
|
412
|
+
if (!existsSync2(path))
|
|
413
|
+
return null;
|
|
414
|
+
try {
|
|
415
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
416
|
+
} catch (error) {
|
|
417
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function normalizeConnection(value) {
|
|
421
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
422
|
+
return null;
|
|
423
|
+
const record = value;
|
|
424
|
+
if (record.kind === "local")
|
|
425
|
+
return { kind: "local", mode: "auto" };
|
|
426
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
427
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
428
|
+
return { kind: "remote", baseUrl };
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function readGlobalConnections(options = {}) {
|
|
433
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
434
|
+
const payload = readJsonFile(path);
|
|
435
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
436
|
+
return { connections: {} };
|
|
437
|
+
}
|
|
438
|
+
const rawConnections = payload.connections;
|
|
439
|
+
const connections = {};
|
|
440
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
441
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
442
|
+
const connection = normalizeConnection(raw);
|
|
443
|
+
if (connection)
|
|
444
|
+
connections[alias] = connection;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return { connections };
|
|
448
|
+
}
|
|
449
|
+
function readRepoConnection(projectRoot) {
|
|
450
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
451
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
452
|
+
return null;
|
|
453
|
+
const record = payload;
|
|
454
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
455
|
+
if (!selected)
|
|
456
|
+
return null;
|
|
457
|
+
return {
|
|
458
|
+
selected,
|
|
459
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
460
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
464
|
+
const repo = readRepoConnection(projectRoot);
|
|
465
|
+
if (!repo)
|
|
466
|
+
return null;
|
|
467
|
+
if (repo.selected === "local")
|
|
468
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
469
|
+
const global = readGlobalConnections(options);
|
|
470
|
+
const connection = global.connections[repo.selected];
|
|
471
|
+
if (!connection) {
|
|
472
|
+
throw new CliError2(`Selected Rig connection "${repo.selected}" was not found. Run \`rig connect list\` or \`rig connect use local\`.`, 1);
|
|
473
|
+
}
|
|
474
|
+
return { alias: repo.selected, connection };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// packages/cli/src/commands/_server-client.ts
|
|
478
|
+
var scopedGitHubBearerTokens = new Map;
|
|
479
|
+
function cleanToken(value) {
|
|
480
|
+
const trimmed = value?.trim();
|
|
481
|
+
return trimmed ? trimmed : null;
|
|
482
|
+
}
|
|
483
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
484
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
485
|
+
if (!existsSync3(path))
|
|
486
|
+
return null;
|
|
487
|
+
try {
|
|
488
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
489
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
490
|
+
} catch {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
495
|
+
const scopedKey = resolve5(projectRoot);
|
|
496
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
497
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
498
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
499
|
+
if (privateSession)
|
|
500
|
+
return privateSession;
|
|
501
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
502
|
+
}
|
|
503
|
+
async function ensureServerForCli(projectRoot) {
|
|
504
|
+
try {
|
|
505
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
506
|
+
if (selected?.connection.kind === "remote") {
|
|
507
|
+
return {
|
|
508
|
+
baseUrl: selected.connection.baseUrl,
|
|
509
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
510
|
+
connectionKind: "remote"
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
514
|
+
return {
|
|
515
|
+
baseUrl: connection.baseUrl,
|
|
516
|
+
authToken: connection.authToken,
|
|
517
|
+
connectionKind: "local"
|
|
518
|
+
};
|
|
519
|
+
} catch (error) {
|
|
520
|
+
if (error instanceof Error) {
|
|
521
|
+
throw new CliError2(error.message, 1);
|
|
522
|
+
}
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function mergeHeaders(headers, authToken) {
|
|
527
|
+
const merged = new Headers(headers);
|
|
528
|
+
if (authToken) {
|
|
529
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
530
|
+
}
|
|
531
|
+
return merged;
|
|
532
|
+
}
|
|
533
|
+
function diagnosticMessage(payload) {
|
|
534
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
535
|
+
return null;
|
|
536
|
+
const record = payload;
|
|
537
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
538
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
539
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
540
|
+
return [];
|
|
541
|
+
const diagnostic = entry;
|
|
542
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
543
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
544
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
545
|
+
});
|
|
546
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
547
|
+
}
|
|
548
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
549
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
550
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
551
|
+
...init,
|
|
552
|
+
headers: mergeHeaders(init.headers, server.authToken)
|
|
553
|
+
});
|
|
554
|
+
const text = await response.text();
|
|
555
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
556
|
+
try {
|
|
557
|
+
return JSON.parse(text);
|
|
558
|
+
} catch {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
})() : null;
|
|
562
|
+
if (!response.ok) {
|
|
563
|
+
const diagnostics = diagnosticMessage(payload);
|
|
564
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
565
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
566
|
+
}
|
|
567
|
+
return payload;
|
|
568
|
+
}
|
|
569
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
570
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
571
|
+
method: "POST",
|
|
572
|
+
headers: { "content-type": "application/json" },
|
|
573
|
+
body: JSON.stringify(input)
|
|
574
|
+
});
|
|
575
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
576
|
+
}
|
|
577
|
+
|
|
391
578
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
579
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
580
|
"Connect",
|
|
@@ -455,12 +642,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
642
|
return 0;
|
|
456
643
|
let copied = 0;
|
|
457
644
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
645
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
646
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
647
|
try {
|
|
461
648
|
if (!statSync(sourcePath).isFile())
|
|
462
649
|
continue;
|
|
463
|
-
|
|
650
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
651
|
copyFileSync(sourcePath, targetPath);
|
|
465
652
|
copied += 1;
|
|
466
653
|
} catch {}
|
|
@@ -499,7 +686,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
686
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
687
|
return {
|
|
501
688
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
689
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
690
|
};
|
|
504
691
|
}
|
|
505
692
|
function positiveInt(value, fallback) {
|
|
@@ -617,9 +804,9 @@ function createCommandRunner(binary) {
|
|
|
617
804
|
const stderrChunks = [];
|
|
618
805
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
806
|
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) =>
|
|
807
|
+
return await new Promise((resolve7) => {
|
|
808
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
809
|
+
child.once("close", (code) => resolve7({
|
|
623
810
|
exitCode: code ?? 1,
|
|
624
811
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
812
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +830,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
830
|
if (!taskId) {
|
|
644
831
|
return { status: "skipped" };
|
|
645
832
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
833
|
+
const configInput = input.config ?? null;
|
|
834
|
+
const config = configInput ?? {};
|
|
835
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
836
|
if (prMode === "off" || prMode === "ask") {
|
|
649
837
|
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
838
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +848,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
848
|
gitCommand
|
|
661
849
|
});
|
|
662
850
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
851
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
852
|
const stage = input.appendStage ?? (() => {
|
|
665
853
|
return;
|
|
666
854
|
});
|
|
@@ -692,8 +880,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
880
|
config,
|
|
693
881
|
sourceTask: input.sourceTask,
|
|
694
882
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
695
|
-
artifactRoot:
|
|
883
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
696
884
|
command: ghCommand,
|
|
885
|
+
gitCommand,
|
|
697
886
|
steerPi,
|
|
698
887
|
lifecycle: {
|
|
699
888
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -742,8 +931,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
742
931
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
743
932
|
})
|
|
744
933
|
}
|
|
745
|
-
}).catch(() => {
|
|
746
|
-
return;
|
|
747
934
|
});
|
|
748
935
|
}
|
|
749
936
|
},
|
|
@@ -762,8 +949,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
762
949
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
763
950
|
})
|
|
764
951
|
}
|
|
765
|
-
}).catch(() => {
|
|
766
|
-
return;
|
|
767
952
|
});
|
|
768
953
|
}
|
|
769
954
|
},
|
|
@@ -792,8 +977,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
792
977
|
errorText: detail
|
|
793
978
|
})
|
|
794
979
|
}
|
|
795
|
-
}).catch(() => {
|
|
796
|
-
|
|
980
|
+
}).catch((error) => {
|
|
981
|
+
try {
|
|
982
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
983
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
984
|
+
title: "Task source needs-attention update failed",
|
|
985
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
986
|
+
tone: "error",
|
|
987
|
+
status: "needs_attention",
|
|
988
|
+
createdAt: new Date().toISOString()
|
|
989
|
+
});
|
|
990
|
+
} catch {}
|
|
797
991
|
});
|
|
798
992
|
}
|
|
799
993
|
return { status: "needs_attention", pr };
|
|
@@ -816,7 +1010,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
816
1010
|
return null;
|
|
817
1011
|
}
|
|
818
1012
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
819
|
-
const summary =
|
|
1013
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
820
1014
|
if (!summary || summary.status !== "fail") {
|
|
821
1015
|
continue;
|
|
822
1016
|
}
|
|
@@ -897,14 +1091,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
897
1091
|
if (!input.taskId || !input.workspaceDir) {
|
|
898
1092
|
return { accepted: false, reason: null };
|
|
899
1093
|
}
|
|
900
|
-
const artifactDir =
|
|
901
|
-
const reviewStatusPath =
|
|
902
|
-
const taskResultPath =
|
|
1094
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1095
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1096
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
903
1097
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
904
1098
|
if (reviewStatus !== "APPROVED") {
|
|
905
1099
|
return { accepted: false, reason: null };
|
|
906
1100
|
}
|
|
907
|
-
const taskResult =
|
|
1101
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
908
1102
|
if (taskResult?.status !== "completed") {
|
|
909
1103
|
return { accepted: false, reason: null };
|
|
910
1104
|
}
|
|
@@ -936,13 +1130,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
936
1130
|
if (!input.taskId || !input.workspaceDir) {
|
|
937
1131
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
938
1132
|
}
|
|
939
|
-
const artifactDir =
|
|
940
|
-
const reviewStatePath =
|
|
941
|
-
const reviewFeedbackPath =
|
|
942
|
-
const reviewStatusPath =
|
|
943
|
-
const failedApproachesPath =
|
|
944
|
-
const validationSummaryPath =
|
|
945
|
-
const reviewState =
|
|
1133
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1134
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1135
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1136
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1137
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1138
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1139
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
946
1140
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
947
1141
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
948
1142
|
if (reviewStatus === "APPROVED") {
|
|
@@ -995,12 +1189,60 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
995
1189
|
}
|
|
996
1190
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
997
1191
|
}
|
|
1192
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1193
|
+
let nextAssistantText = input.assistantText;
|
|
1194
|
+
if (input.record.type === "message_update") {
|
|
1195
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1196
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1197
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1198
|
+
}
|
|
1199
|
+
} else if (input.record.type === "stream_event") {
|
|
1200
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1201
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1202
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1203
|
+
nextAssistantText += delta.text;
|
|
1204
|
+
}
|
|
1205
|
+
} else if (input.record.type === "assistant") {
|
|
1206
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1207
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1208
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1209
|
+
if (fullText.length > nextAssistantText.length)
|
|
1210
|
+
nextAssistantText = fullText;
|
|
1211
|
+
}
|
|
1212
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1213
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1214
|
+
id: input.messageId,
|
|
1215
|
+
type: "assistant_message",
|
|
1216
|
+
text: nextAssistantText,
|
|
1217
|
+
state: "streaming",
|
|
1218
|
+
createdAt: new Date().toISOString()
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
return nextAssistantText;
|
|
1222
|
+
}
|
|
1223
|
+
function appendToolTimelineFromLog(input) {
|
|
1224
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1225
|
+
if (title !== "Tool activity")
|
|
1226
|
+
return;
|
|
1227
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1228
|
+
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;
|
|
1229
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1230
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1231
|
+
id: `tool:${logId}`,
|
|
1232
|
+
type: "tool_execution_update",
|
|
1233
|
+
toolName,
|
|
1234
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1235
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1236
|
+
payload,
|
|
1237
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
998
1240
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
999
|
-
if (!
|
|
1241
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
1000
1242
|
return null;
|
|
1001
1243
|
}
|
|
1002
1244
|
try {
|
|
1003
|
-
const status =
|
|
1245
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1004
1246
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1005
1247
|
} catch {
|
|
1006
1248
|
return null;
|
|
@@ -1018,7 +1260,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1018
1260
|
function runSourceTaskIdentity(sourceTask) {
|
|
1019
1261
|
return sourceTask;
|
|
1020
1262
|
}
|
|
1021
|
-
|
|
1263
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1264
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1265
|
+
return null;
|
|
1266
|
+
const record = sourceTask;
|
|
1267
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1268
|
+
if (direct?.trim())
|
|
1269
|
+
return direct.trim();
|
|
1270
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1271
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1272
|
+
}
|
|
1273
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1274
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1275
|
+
id: input.taskId,
|
|
1276
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1277
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1278
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1279
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1280
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1281
|
+
});
|
|
1282
|
+
if (serverResult.ok === false) {
|
|
1283
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
updated: serverResult.ok !== false,
|
|
1287
|
+
taskId: input.taskId,
|
|
1288
|
+
status: input.update.status,
|
|
1289
|
+
source: "server",
|
|
1290
|
+
sourceKind: "server",
|
|
1291
|
+
projectSync: serverResult.projectSync
|
|
1292
|
+
};
|
|
1293
|
+
};
|
|
1294
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1022
1295
|
if (!taskId)
|
|
1023
1296
|
return;
|
|
1024
1297
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1187,15 +1460,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1187
1460
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1188
1461
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1189
1462
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1190
|
-
const planningArtifactPath =
|
|
1463
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1191
1464
|
const persistedPlanning = {
|
|
1192
1465
|
...planningClassification,
|
|
1193
1466
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1194
1467
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1195
1468
|
classifiedAt: new Date().toISOString()
|
|
1196
1469
|
};
|
|
1197
|
-
|
|
1198
|
-
|
|
1470
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1471
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1199
1472
|
`, "utf8");
|
|
1200
1473
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1201
1474
|
prompt = `${prompt}
|
|
@@ -1245,7 +1518,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1245
1518
|
let verificationStarted = false;
|
|
1246
1519
|
let reviewStarted = false;
|
|
1247
1520
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1248
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1521
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1249
1522
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1250
1523
|
let latestProviderCommand = null;
|
|
1251
1524
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
@@ -1328,10 +1601,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1328
1601
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1329
1602
|
status: "running",
|
|
1330
1603
|
worktreePath: latestRuntimeWorkspace,
|
|
1331
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1604
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1332
1605
|
logRoot: latestLogsDir,
|
|
1333
|
-
sessionPath: latestSessionDir ?
|
|
1334
|
-
sessionLogPath: latestLogsDir ?
|
|
1606
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1607
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1335
1608
|
branch: runtimeId
|
|
1336
1609
|
});
|
|
1337
1610
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1339,8 +1612,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1339
1612
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1340
1613
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1341
1614
|
if (readyFile) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1615
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1616
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1344
1617
|
`, "utf8");
|
|
1345
1618
|
}
|
|
1346
1619
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1488,7 +1761,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1488
1761
|
if (providerLogs.length > 0) {
|
|
1489
1762
|
for (const providerLog of providerLogs) {
|
|
1490
1763
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1764
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1491
1765
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1766
|
+
if (providerLog.title === "Tool activity")
|
|
1767
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1492
1768
|
}
|
|
1493
1769
|
}
|
|
1494
1770
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1646,7 +1922,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1646
1922
|
let acceptedArtifactObservedAt = null;
|
|
1647
1923
|
let acceptedArtifactPollTimer = null;
|
|
1648
1924
|
let acceptedArtifactKillTimer = null;
|
|
1649
|
-
const attemptExit = await new Promise((
|
|
1925
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1650
1926
|
let settled = false;
|
|
1651
1927
|
const settle = (result) => {
|
|
1652
1928
|
if (settled)
|
|
@@ -1654,7 +1930,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1654
1930
|
settled = true;
|
|
1655
1931
|
if (acceptedArtifactPollTimer)
|
|
1656
1932
|
clearInterval(acceptedArtifactPollTimer);
|
|
1657
|
-
|
|
1933
|
+
resolve7(result);
|
|
1658
1934
|
};
|
|
1659
1935
|
const pollAcceptedArtifacts = () => {
|
|
1660
1936
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1727,7 +2003,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1727
2003
|
});
|
|
1728
2004
|
for (const pendingLog of pendingLogs) {
|
|
1729
2005
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2006
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1730
2007
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2008
|
+
if (pendingLog.title === "Tool activity")
|
|
2009
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1731
2010
|
}
|
|
1732
2011
|
process.off("SIGTERM", forwardSigterm);
|
|
1733
2012
|
if (attemptExit.error) {
|
|
@@ -1853,8 +2132,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1853
2132
|
}
|
|
1854
2133
|
if (planningClassification.planningRequired) {
|
|
1855
2134
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1856
|
-
const expectedPlanPath =
|
|
1857
|
-
if (!
|
|
2135
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2136
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1858
2137
|
const failedAt = new Date().toISOString();
|
|
1859
2138
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1860
2139
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1874,6 +2153,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1874
2153
|
throw new CliError2(failureDetail, 1);
|
|
1875
2154
|
}
|
|
1876
2155
|
}
|
|
2156
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2157
|
+
appendPiStageLog({
|
|
2158
|
+
projectRoot: context.projectRoot,
|
|
2159
|
+
runId: input.runId,
|
|
2160
|
+
stage: "Validate",
|
|
2161
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2162
|
+
status: "completed"
|
|
2163
|
+
});
|
|
2164
|
+
if (verificationAction && !reviewStarted) {
|
|
2165
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2166
|
+
}
|
|
2167
|
+
if (!reviewAction) {
|
|
2168
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2169
|
+
}
|
|
2170
|
+
if (reviewAction) {
|
|
2171
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2172
|
+
}
|
|
2173
|
+
const requestedAt = new Date().toISOString();
|
|
2174
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2175
|
+
status: "reviewing",
|
|
2176
|
+
completedAt: null,
|
|
2177
|
+
errorText: null,
|
|
2178
|
+
serverCloseout: {
|
|
2179
|
+
status: "pending",
|
|
2180
|
+
phase: "queued",
|
|
2181
|
+
requestedAt,
|
|
2182
|
+
updatedAt: requestedAt,
|
|
2183
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2184
|
+
branch: latestRuntimeBranch,
|
|
2185
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2186
|
+
}
|
|
2187
|
+
});
|
|
2188
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2189
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2190
|
+
title: "Server-owned closeout requested",
|
|
2191
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2192
|
+
tone: "info",
|
|
2193
|
+
status: "reviewing",
|
|
2194
|
+
createdAt: requestedAt,
|
|
2195
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2196
|
+
});
|
|
2197
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2198
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2199
|
+
await context.emitEvent("command.finished", {
|
|
2200
|
+
command: [
|
|
2201
|
+
"rig",
|
|
2202
|
+
"server",
|
|
2203
|
+
"task-run",
|
|
2204
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2205
|
+
...input.title ? ["--title", input.title] : []
|
|
2206
|
+
],
|
|
2207
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2208
|
+
exitCode: 0,
|
|
2209
|
+
durationMs: 0,
|
|
2210
|
+
startedAt,
|
|
2211
|
+
finishedAt: requestedAt
|
|
2212
|
+
});
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
1877
2215
|
const runPiPrFeedbackFix = async (message) => {
|
|
1878
2216
|
appendPiStageLog({
|
|
1879
2217
|
projectRoot: context.projectRoot,
|
|
@@ -1901,11 +2239,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1901
2239
|
child.stdin.write(message);
|
|
1902
2240
|
}
|
|
1903
2241
|
child.stdin.end();
|
|
2242
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2243
|
+
let feedbackAssistantText = "";
|
|
2244
|
+
const feedbackPendingToolUses = new Map;
|
|
1904
2245
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1905
2246
|
stdout.on("line", (line) => {
|
|
1906
2247
|
const trimmed = line.trim();
|
|
1907
2248
|
if (!trimmed)
|
|
1908
2249
|
return;
|
|
2250
|
+
try {
|
|
2251
|
+
const record = JSON.parse(trimmed);
|
|
2252
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2253
|
+
runId: input.runId,
|
|
2254
|
+
record,
|
|
2255
|
+
createdAtFallback: new Date().toISOString(),
|
|
2256
|
+
status: "reviewing",
|
|
2257
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2258
|
+
});
|
|
2259
|
+
for (const providerLog of providerLogs) {
|
|
2260
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2261
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2262
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2263
|
+
if (providerLog.title === "Tool activity")
|
|
2264
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2265
|
+
}
|
|
2266
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2267
|
+
projectRoot: context.projectRoot,
|
|
2268
|
+
runId: input.runId,
|
|
2269
|
+
messageId: feedbackAssistantMessageId,
|
|
2270
|
+
record,
|
|
2271
|
+
assistantText: feedbackAssistantText
|
|
2272
|
+
});
|
|
2273
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2274
|
+
if (hadAssistantDelta) {
|
|
2275
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2276
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2277
|
+
}
|
|
2278
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2279
|
+
return;
|
|
2280
|
+
} catch {}
|
|
1909
2281
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1910
2282
|
id: nextRunLogId(),
|
|
1911
2283
|
title: "Pi PR feedback fix output",
|
|
@@ -1931,10 +2303,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1931
2303
|
});
|
|
1932
2304
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1933
2305
|
});
|
|
1934
|
-
const exitCode = await new Promise((
|
|
1935
|
-
child.once("error", () =>
|
|
1936
|
-
child.once("close", (code) =>
|
|
2306
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2307
|
+
child.once("error", () => resolve7(1));
|
|
2308
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1937
2309
|
});
|
|
2310
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2311
|
+
runId: input.runId,
|
|
2312
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2313
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2314
|
+
})) {
|
|
2315
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2316
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2317
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2318
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2319
|
+
}
|
|
2320
|
+
if (feedbackAssistantText.trim()) {
|
|
2321
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2322
|
+
id: feedbackAssistantMessageId,
|
|
2323
|
+
type: "assistant_message",
|
|
2324
|
+
text: feedbackAssistantText,
|
|
2325
|
+
state: "completed",
|
|
2326
|
+
createdAt: new Date().toISOString(),
|
|
2327
|
+
completedAt: new Date().toISOString()
|
|
2328
|
+
});
|
|
2329
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2330
|
+
}
|
|
1938
2331
|
if (exitCode !== 0) {
|
|
1939
2332
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1940
2333
|
}
|