@h-rig/cli 0.0.6-alpha.4 → 0.0.6-alpha.41
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 +3905 -1193
- 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 +445 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.js +916 -56
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +880 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +665 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +676 -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 +2 -2
- package/dist/src/commands/_server-client.js +152 -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 +8 -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 +237 -23
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/pi.js +168 -0
- package/dist/src/commands/plugin.js +81 -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 +1214 -123
- package/dist/src/commands/server.js +222 -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 +630 -71
- package/dist/src/commands/task.js +1653 -252
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +3884 -1166
- package/dist/src/index.js +3898 -1189
- 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,14 +37,14 @@ 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,
|
|
49
46
|
commitRunChanges,
|
|
47
|
+
pushBranchSyncedWithOrigin,
|
|
50
48
|
runPrAutomation
|
|
51
49
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
52
50
|
|
|
@@ -60,6 +58,7 @@ import {
|
|
|
60
58
|
writeJsonFile as writeJsonFile2
|
|
61
59
|
} from "@rig/runtime/control-plane/authority-files";
|
|
62
60
|
import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
|
|
61
|
+
import { readPriorPrProgressPromptSection } from "@rig/runtime/control-plane/prompt-context";
|
|
63
62
|
import { buildProviderTaskRunInstructionLines } from "@rig/runtime/control-plane/provider/runtime-instructions";
|
|
64
63
|
import { loadRigTaskRunSkillBody } from "@rig/runtime/control-plane/provider/rig-task-run-skill";
|
|
65
64
|
|
|
@@ -317,6 +316,7 @@ ${acceptance}` : null,
|
|
|
317
316
|
${sourceContractLines.join(`
|
|
318
317
|
`)}` : null,
|
|
319
318
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
319
|
+
readPriorPrProgressPromptSection(input.projectRoot, input.taskId),
|
|
320
320
|
initialPrompt ? `Additional operator guidance:
|
|
321
321
|
${initialPrompt}` : null,
|
|
322
322
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -388,6 +388,194 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
388
388
|
`);
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
// packages/cli/src/commands/_server-client.ts
|
|
392
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
393
|
+
import { resolve as resolve5 } from "path";
|
|
394
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
395
|
+
|
|
396
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
397
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
398
|
+
import { homedir } from "os";
|
|
399
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
400
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
401
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
402
|
+
if (explicit)
|
|
403
|
+
return resolve4(explicit);
|
|
404
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
405
|
+
if (stateDir)
|
|
406
|
+
return resolve4(stateDir, "connections.json");
|
|
407
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
408
|
+
}
|
|
409
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
410
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
411
|
+
}
|
|
412
|
+
function readJsonFile(path) {
|
|
413
|
+
if (!existsSync2(path))
|
|
414
|
+
return null;
|
|
415
|
+
try {
|
|
416
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
417
|
+
} catch (error) {
|
|
418
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function normalizeConnection(value) {
|
|
422
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
423
|
+
return null;
|
|
424
|
+
const record = value;
|
|
425
|
+
if (record.kind === "local")
|
|
426
|
+
return { kind: "local", mode: "auto" };
|
|
427
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
428
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
429
|
+
return { kind: "remote", baseUrl };
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
function readGlobalConnections(options = {}) {
|
|
434
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
435
|
+
const payload = readJsonFile(path);
|
|
436
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
437
|
+
return { connections: {} };
|
|
438
|
+
}
|
|
439
|
+
const rawConnections = payload.connections;
|
|
440
|
+
const connections = {};
|
|
441
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
442
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
443
|
+
const connection = normalizeConnection(raw);
|
|
444
|
+
if (connection)
|
|
445
|
+
connections[alias] = connection;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return { connections };
|
|
449
|
+
}
|
|
450
|
+
function readRepoConnection(projectRoot) {
|
|
451
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
452
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
453
|
+
return null;
|
|
454
|
+
const record = payload;
|
|
455
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
456
|
+
if (!selected)
|
|
457
|
+
return null;
|
|
458
|
+
return {
|
|
459
|
+
selected,
|
|
460
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
461
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
465
|
+
const repo = readRepoConnection(projectRoot);
|
|
466
|
+
if (!repo)
|
|
467
|
+
return null;
|
|
468
|
+
if (repo.selected === "local")
|
|
469
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
470
|
+
const global = readGlobalConnections(options);
|
|
471
|
+
const connection = global.connections[repo.selected];
|
|
472
|
+
if (!connection) {
|
|
473
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
474
|
+
}
|
|
475
|
+
return { alias: repo.selected, connection };
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// packages/cli/src/commands/_server-client.ts
|
|
479
|
+
var scopedGitHubBearerTokens = new Map;
|
|
480
|
+
function cleanToken(value) {
|
|
481
|
+
const trimmed = value?.trim();
|
|
482
|
+
return trimmed ? trimmed : null;
|
|
483
|
+
}
|
|
484
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
485
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
486
|
+
if (!existsSync3(path))
|
|
487
|
+
return null;
|
|
488
|
+
try {
|
|
489
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
490
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
491
|
+
} catch {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
496
|
+
const scopedKey = resolve5(projectRoot);
|
|
497
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
498
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
499
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
500
|
+
if (privateSession)
|
|
501
|
+
return privateSession;
|
|
502
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
503
|
+
}
|
|
504
|
+
async function ensureServerForCli(projectRoot) {
|
|
505
|
+
try {
|
|
506
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
507
|
+
if (selected?.connection.kind === "remote") {
|
|
508
|
+
return {
|
|
509
|
+
baseUrl: selected.connection.baseUrl,
|
|
510
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
511
|
+
connectionKind: "remote"
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
515
|
+
return {
|
|
516
|
+
baseUrl: connection.baseUrl,
|
|
517
|
+
authToken: connection.authToken,
|
|
518
|
+
connectionKind: "local"
|
|
519
|
+
};
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (error instanceof Error) {
|
|
522
|
+
throw new CliError2(error.message, 1);
|
|
523
|
+
}
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function mergeHeaders(headers, authToken) {
|
|
528
|
+
const merged = new Headers(headers);
|
|
529
|
+
if (authToken) {
|
|
530
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
531
|
+
}
|
|
532
|
+
return merged;
|
|
533
|
+
}
|
|
534
|
+
function diagnosticMessage(payload) {
|
|
535
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
536
|
+
return null;
|
|
537
|
+
const record = payload;
|
|
538
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
539
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
540
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
541
|
+
return [];
|
|
542
|
+
const diagnostic = entry;
|
|
543
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
544
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
545
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
546
|
+
});
|
|
547
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
548
|
+
}
|
|
549
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
550
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
551
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
552
|
+
...init,
|
|
553
|
+
headers: mergeHeaders(init.headers, server.authToken)
|
|
554
|
+
});
|
|
555
|
+
const text = await response.text();
|
|
556
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
557
|
+
try {
|
|
558
|
+
return JSON.parse(text);
|
|
559
|
+
} catch {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
})() : null;
|
|
563
|
+
if (!response.ok) {
|
|
564
|
+
const diagnostics = diagnosticMessage(payload);
|
|
565
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
566
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
567
|
+
}
|
|
568
|
+
return payload;
|
|
569
|
+
}
|
|
570
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
571
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
572
|
+
method: "POST",
|
|
573
|
+
headers: { "content-type": "application/json" },
|
|
574
|
+
body: JSON.stringify(input)
|
|
575
|
+
});
|
|
576
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
577
|
+
}
|
|
578
|
+
|
|
391
579
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
580
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
581
|
"Connect",
|
|
@@ -431,6 +619,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
619
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
620
|
RIG_TASK_ID: input.taskId,
|
|
433
621
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
622
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
623
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
624
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
625
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +644,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
644
|
return 0;
|
|
456
645
|
let copied = 0;
|
|
457
646
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
647
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
648
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
649
|
try {
|
|
461
650
|
if (!statSync(sourcePath).isFile())
|
|
462
651
|
continue;
|
|
463
|
-
|
|
652
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
653
|
copyFileSync(sourcePath, targetPath);
|
|
465
654
|
copied += 1;
|
|
466
655
|
} catch {}
|
|
@@ -499,7 +688,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
688
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
689
|
return {
|
|
501
690
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
691
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
692
|
};
|
|
504
693
|
}
|
|
505
694
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +696,7 @@ function positiveInt(value, fallback) {
|
|
|
507
696
|
}
|
|
508
697
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
698
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
699
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
700
|
return {
|
|
512
701
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
702
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +790,6 @@ function appendPiStageLog(input) {
|
|
|
601
790
|
});
|
|
602
791
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
792
|
}
|
|
604
|
-
async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
605
|
-
const result = await command(args, { cwd });
|
|
606
|
-
if (result.exitCode !== 0) {
|
|
607
|
-
throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
793
|
function createCommandRunner(binary) {
|
|
611
794
|
return async (args, options) => {
|
|
612
795
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +800,9 @@ function createCommandRunner(binary) {
|
|
|
617
800
|
const stderrChunks = [];
|
|
618
801
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
802
|
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) =>
|
|
803
|
+
return await new Promise((resolve7) => {
|
|
804
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
805
|
+
child.once("close", (code) => resolve7({
|
|
623
806
|
exitCode: code ?? 1,
|
|
624
807
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
808
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +826,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
826
|
if (!taskId) {
|
|
644
827
|
return { status: "skipped" };
|
|
645
828
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
829
|
+
const configInput = input.config ?? null;
|
|
830
|
+
const config = configInput ?? {};
|
|
831
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
832
|
if (prMode === "off" || prMode === "ask") {
|
|
649
833
|
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
834
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +844,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
844
|
gitCommand
|
|
661
845
|
});
|
|
662
846
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
847
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
848
|
const stage = input.appendStage ?? (() => {
|
|
665
849
|
return;
|
|
666
850
|
});
|
|
@@ -682,7 +866,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
866
|
command: gitCommand
|
|
683
867
|
});
|
|
684
868
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
869
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
870
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
871
|
const pr = await prAutomation({
|
|
688
872
|
projectRoot: workspace,
|
|
@@ -692,7 +876,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
876
|
config,
|
|
693
877
|
sourceTask: input.sourceTask,
|
|
694
878
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
879
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
880
|
command: ghCommand,
|
|
881
|
+
gitCommand,
|
|
696
882
|
steerPi,
|
|
697
883
|
lifecycle: {
|
|
698
884
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +927,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
927
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
928
|
})
|
|
743
929
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
930
|
});
|
|
747
931
|
}
|
|
748
932
|
},
|
|
@@ -761,8 +945,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
945
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
946
|
})
|
|
763
947
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
948
|
});
|
|
767
949
|
}
|
|
768
950
|
},
|
|
@@ -791,8 +973,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
973
|
errorText: detail
|
|
792
974
|
})
|
|
793
975
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
976
|
+
}).catch((error) => {
|
|
977
|
+
try {
|
|
978
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
979
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
980
|
+
title: "Task source needs-attention update failed",
|
|
981
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
982
|
+
tone: "error",
|
|
983
|
+
status: "needs_attention",
|
|
984
|
+
createdAt: new Date().toISOString()
|
|
985
|
+
});
|
|
986
|
+
} catch {}
|
|
796
987
|
});
|
|
797
988
|
}
|
|
798
989
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1006,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1006
|
return null;
|
|
816
1007
|
}
|
|
817
1008
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1009
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1010
|
if (!summary || summary.status !== "fail") {
|
|
820
1011
|
continue;
|
|
821
1012
|
}
|
|
@@ -896,14 +1087,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1087
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1088
|
return { accepted: false, reason: null };
|
|
898
1089
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1090
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1091
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1092
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1093
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1094
|
if (reviewStatus !== "APPROVED") {
|
|
904
1095
|
return { accepted: false, reason: null };
|
|
905
1096
|
}
|
|
906
|
-
const taskResult =
|
|
1097
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1098
|
if (taskResult?.status !== "completed") {
|
|
908
1099
|
return { accepted: false, reason: null };
|
|
909
1100
|
}
|
|
@@ -935,13 +1126,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1126
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1127
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1128
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1129
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1130
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1131
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1132
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1133
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1134
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1135
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1136
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1137
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1138
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1185,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1185
|
}
|
|
995
1186
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1187
|
}
|
|
1188
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1189
|
+
let nextAssistantText = input.assistantText;
|
|
1190
|
+
if (input.record.type === "message_update") {
|
|
1191
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1192
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1193
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1194
|
+
}
|
|
1195
|
+
} else if (input.record.type === "stream_event") {
|
|
1196
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1197
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1198
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1199
|
+
nextAssistantText += delta.text;
|
|
1200
|
+
}
|
|
1201
|
+
} else if (input.record.type === "assistant") {
|
|
1202
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1203
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1204
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1205
|
+
if (fullText.length > nextAssistantText.length)
|
|
1206
|
+
nextAssistantText = fullText;
|
|
1207
|
+
}
|
|
1208
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1209
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1210
|
+
id: input.messageId,
|
|
1211
|
+
type: "assistant_message",
|
|
1212
|
+
text: nextAssistantText,
|
|
1213
|
+
state: "streaming",
|
|
1214
|
+
createdAt: new Date().toISOString()
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
return nextAssistantText;
|
|
1218
|
+
}
|
|
1219
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1220
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1221
|
+
if (type === "response") {
|
|
1222
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1223
|
+
const success = input.record.success !== false;
|
|
1224
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1225
|
+
return true;
|
|
1226
|
+
}
|
|
1227
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1228
|
+
id: input.nextRunLogId(),
|
|
1229
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1230
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1231
|
+
tone: success ? "tool" : "error",
|
|
1232
|
+
status: input.status,
|
|
1233
|
+
payload: input.record,
|
|
1234
|
+
createdAt: new Date().toISOString()
|
|
1235
|
+
});
|
|
1236
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1237
|
+
return true;
|
|
1238
|
+
}
|
|
1239
|
+
if (type !== "extension_ui_request")
|
|
1240
|
+
return false;
|
|
1241
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1242
|
+
let title = "Pi UI event";
|
|
1243
|
+
let detail = method;
|
|
1244
|
+
let tone = "info";
|
|
1245
|
+
if (method === "notify") {
|
|
1246
|
+
title = "Pi notification";
|
|
1247
|
+
detail = String(input.record.message ?? "");
|
|
1248
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1249
|
+
} else if (method === "setStatus") {
|
|
1250
|
+
title = "Pi UI status";
|
|
1251
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1252
|
+
tone = "tool";
|
|
1253
|
+
} else if (method === "setWidget") {
|
|
1254
|
+
title = "Pi UI widget";
|
|
1255
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1256
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1257
|
+
tone = "tool";
|
|
1258
|
+
} else if (method === "setTitle") {
|
|
1259
|
+
title = "Pi UI title";
|
|
1260
|
+
detail = String(input.record.title ?? "");
|
|
1261
|
+
tone = "tool";
|
|
1262
|
+
} else if (method === "set_editor_text") {
|
|
1263
|
+
title = "Pi editor update";
|
|
1264
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1265
|
+
tone = "tool";
|
|
1266
|
+
} else {
|
|
1267
|
+
title = "Pi UI request";
|
|
1268
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1269
|
+
}
|
|
1270
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1271
|
+
id: input.nextRunLogId(),
|
|
1272
|
+
title,
|
|
1273
|
+
detail,
|
|
1274
|
+
tone,
|
|
1275
|
+
status: input.status,
|
|
1276
|
+
payload: input.record,
|
|
1277
|
+
createdAt: new Date().toISOString()
|
|
1278
|
+
});
|
|
1279
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1280
|
+
return true;
|
|
1281
|
+
}
|
|
1282
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1283
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1284
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1285
|
+
return false;
|
|
1286
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1287
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1288
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1289
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1290
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1291
|
+
type,
|
|
1292
|
+
toolName,
|
|
1293
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1294
|
+
createdAt: new Date().toISOString()
|
|
1295
|
+
});
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1299
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1300
|
+
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");
|
|
1301
|
+
}
|
|
1302
|
+
function appendToolTimelineFromLog(input) {
|
|
1303
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1304
|
+
if (title !== "Tool activity")
|
|
1305
|
+
return;
|
|
1306
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1307
|
+
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;
|
|
1308
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1309
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1310
|
+
id: `tool:${logId}`,
|
|
1311
|
+
type: "tool_execution_update",
|
|
1312
|
+
toolName,
|
|
1313
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1314
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1315
|
+
payload,
|
|
1316
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
997
1319
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1320
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1321
|
return null;
|
|
1000
1322
|
}
|
|
1001
1323
|
try {
|
|
1002
|
-
const status =
|
|
1324
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1325
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1326
|
} catch {
|
|
1005
1327
|
return null;
|
|
@@ -1017,7 +1339,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1339
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1340
|
return sourceTask;
|
|
1019
1341
|
}
|
|
1020
|
-
|
|
1342
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1343
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1344
|
+
return null;
|
|
1345
|
+
const record = sourceTask;
|
|
1346
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1347
|
+
if (direct?.trim())
|
|
1348
|
+
return direct.trim();
|
|
1349
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1350
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1351
|
+
}
|
|
1352
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1353
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1354
|
+
id: input.taskId,
|
|
1355
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1356
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1357
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1358
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1359
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1360
|
+
});
|
|
1361
|
+
if (serverResult.ok === false) {
|
|
1362
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1363
|
+
}
|
|
1364
|
+
return {
|
|
1365
|
+
updated: serverResult.ok !== false,
|
|
1366
|
+
taskId: input.taskId,
|
|
1367
|
+
status: input.update.status,
|
|
1368
|
+
source: "server",
|
|
1369
|
+
sourceKind: "server",
|
|
1370
|
+
projectSync: serverResult.projectSync
|
|
1371
|
+
};
|
|
1372
|
+
};
|
|
1373
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1374
|
if (!taskId)
|
|
1022
1375
|
return;
|
|
1023
1376
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1084,6 +1437,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1084
1437
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
1085
1438
|
const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
|
|
1086
1439
|
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
1440
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
1087
1441
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
1088
1442
|
let prompt = buildRunPrompt({
|
|
1089
1443
|
projectRoot: context.projectRoot,
|
|
@@ -1106,11 +1460,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1106
1460
|
...input.model ? ["--model", input.model] : [],
|
|
1107
1461
|
"--prompt"
|
|
1108
1462
|
] : input.runtimeAdapter === "pi" ? [
|
|
1109
|
-
"
|
|
1110
|
-
"--verbose",
|
|
1111
|
-
"--mode",
|
|
1112
|
-
"json",
|
|
1113
|
-
"--no-session",
|
|
1463
|
+
"__rig_pi_session_daemon__",
|
|
1114
1464
|
...input.model ? ["--model", input.model] : []
|
|
1115
1465
|
] : [
|
|
1116
1466
|
"--print",
|
|
@@ -1185,15 +1535,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1185
1535
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1186
1536
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1187
1537
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1188
|
-
const planningArtifactPath =
|
|
1538
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1189
1539
|
const persistedPlanning = {
|
|
1190
1540
|
...planningClassification,
|
|
1191
1541
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1192
1542
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1193
1543
|
classifiedAt: new Date().toISOString()
|
|
1194
1544
|
};
|
|
1195
|
-
|
|
1196
|
-
|
|
1545
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1546
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1197
1547
|
`, "utf8");
|
|
1198
1548
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1199
1549
|
prompt = `${prompt}
|
|
@@ -1207,7 +1557,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1207
1557
|
projectRoot: context.projectRoot,
|
|
1208
1558
|
runId: input.runId,
|
|
1209
1559
|
stage,
|
|
1210
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1560
|
+
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,
|
|
1211
1561
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1212
1562
|
});
|
|
1213
1563
|
}
|
|
@@ -1243,11 +1593,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1243
1593
|
let verificationStarted = false;
|
|
1244
1594
|
let reviewStarted = false;
|
|
1245
1595
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1246
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1596
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1247
1597
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1248
1598
|
let latestProviderCommand = null;
|
|
1249
1599
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1250
1600
|
let snapshotSidecarPromise = null;
|
|
1601
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1251
1602
|
let dirtyBaselineApplied = false;
|
|
1252
1603
|
const childEnv = {
|
|
1253
1604
|
...process.env,
|
|
@@ -1300,6 +1651,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1300
1651
|
detail: detail ?? "Verifier review is running."
|
|
1301
1652
|
});
|
|
1302
1653
|
};
|
|
1654
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1303
1655
|
const handleWrapperEvent = (rawPayload) => {
|
|
1304
1656
|
let event = null;
|
|
1305
1657
|
try {
|
|
@@ -1315,6 +1667,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1315
1667
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1316
1668
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1317
1669
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1670
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1318
1671
|
latestRuntimeBranch = runtimeId;
|
|
1319
1672
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1320
1673
|
runtimeId: runtimeId ?? null,
|
|
@@ -1326,10 +1679,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1326
1679
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1327
1680
|
status: "running",
|
|
1328
1681
|
worktreePath: latestRuntimeWorkspace,
|
|
1329
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1682
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1330
1683
|
logRoot: latestLogsDir,
|
|
1331
|
-
sessionPath: latestSessionDir ?
|
|
1332
|
-
sessionLogPath: latestLogsDir ?
|
|
1684
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1685
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1333
1686
|
branch: runtimeId
|
|
1334
1687
|
});
|
|
1335
1688
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1337,8 +1690,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1337
1690
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1338
1691
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1339
1692
|
if (readyFile) {
|
|
1340
|
-
|
|
1341
|
-
|
|
1693
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1694
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1342
1695
|
`, "utf8");
|
|
1343
1696
|
}
|
|
1344
1697
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1352,7 +1705,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1352
1705
|
});
|
|
1353
1706
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1354
1707
|
}
|
|
1355
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1708
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1356
1709
|
snapshotSidecarPromise = (async () => {
|
|
1357
1710
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1358
1711
|
projectRoot: context.projectRoot,
|
|
@@ -1434,9 +1787,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1434
1787
|
}
|
|
1435
1788
|
return true;
|
|
1436
1789
|
}
|
|
1790
|
+
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") {
|
|
1791
|
+
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";
|
|
1792
|
+
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");
|
|
1793
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1794
|
+
id: nextRunLogId(),
|
|
1795
|
+
title,
|
|
1796
|
+
detail,
|
|
1797
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1798
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1799
|
+
payload: {
|
|
1800
|
+
eventType: event.type,
|
|
1801
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1802
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1803
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1804
|
+
},
|
|
1805
|
+
createdAt: new Date().toISOString()
|
|
1806
|
+
});
|
|
1807
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1808
|
+
return true;
|
|
1809
|
+
}
|
|
1810
|
+
if (event.type === "pi.session.ready") {
|
|
1811
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1812
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1813
|
+
if (privateMetadata) {
|
|
1814
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1815
|
+
piSession: publicMetadata,
|
|
1816
|
+
piSessionPrivate: privateMetadata
|
|
1817
|
+
});
|
|
1818
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1819
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1820
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1821
|
+
title: "Worker Pi session ready",
|
|
1822
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1823
|
+
tone: "info",
|
|
1824
|
+
status: "running",
|
|
1825
|
+
payload: {
|
|
1826
|
+
sessionId: sessionId || null,
|
|
1827
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1828
|
+
},
|
|
1829
|
+
createdAt: new Date().toISOString()
|
|
1830
|
+
});
|
|
1831
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1832
|
+
}
|
|
1833
|
+
return true;
|
|
1834
|
+
}
|
|
1835
|
+
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") {
|
|
1836
|
+
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";
|
|
1837
|
+
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")}`;
|
|
1838
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1839
|
+
id: nextRunLogId(),
|
|
1840
|
+
title,
|
|
1841
|
+
detail,
|
|
1842
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1843
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1844
|
+
payload,
|
|
1845
|
+
createdAt: new Date().toISOString()
|
|
1846
|
+
});
|
|
1847
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1848
|
+
return true;
|
|
1849
|
+
}
|
|
1437
1850
|
return false;
|
|
1438
1851
|
};
|
|
1439
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1440
1852
|
const handleAgentStdoutLine = (line) => {
|
|
1441
1853
|
const trimmed = line.trim();
|
|
1442
1854
|
if (!trimmed)
|
|
@@ -1470,6 +1882,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1470
1882
|
try {
|
|
1471
1883
|
const record = JSON.parse(trimmed);
|
|
1472
1884
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1885
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1886
|
+
projectRoot: context.projectRoot,
|
|
1887
|
+
runId: input.runId,
|
|
1888
|
+
record,
|
|
1889
|
+
status: liveLogStatus,
|
|
1890
|
+
nextRunLogId
|
|
1891
|
+
})) {
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1895
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1473
1898
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1474
1899
|
runId: input.runId,
|
|
1475
1900
|
record,
|
|
@@ -1486,7 +1911,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1486
1911
|
if (providerLogs.length > 0) {
|
|
1487
1912
|
for (const providerLog of providerLogs) {
|
|
1488
1913
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1914
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1489
1915
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1916
|
+
if (providerLog.title === "Tool activity")
|
|
1917
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1490
1918
|
}
|
|
1491
1919
|
}
|
|
1492
1920
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1538,6 +1966,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1538
1966
|
return;
|
|
1539
1967
|
}
|
|
1540
1968
|
}
|
|
1969
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1541
1972
|
if (record.type === "assistant") {
|
|
1542
1973
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1543
1974
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1574,7 +2005,18 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1574
2005
|
let reviewFailureDetail = null;
|
|
1575
2006
|
const stderrLines = [];
|
|
1576
2007
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
1577
|
-
if (resumeMode &&
|
|
2008
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
2009
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2010
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
2011
|
+
title: "Resume continuing from closeout phase",
|
|
2012
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
2013
|
+
tone: "info",
|
|
2014
|
+
status: resumePreviousStatus,
|
|
2015
|
+
createdAt: new Date().toISOString()
|
|
2016
|
+
});
|
|
2017
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
2018
|
+
exit = { code: 0, signal: null };
|
|
2019
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
1578
2020
|
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
1579
2021
|
taskId: input.taskId ?? runtimeTaskId,
|
|
1580
2022
|
workspaceDir: latestRuntimeWorkspace
|
|
@@ -1633,7 +2075,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1633
2075
|
let acceptedArtifactObservedAt = null;
|
|
1634
2076
|
let acceptedArtifactPollTimer = null;
|
|
1635
2077
|
let acceptedArtifactKillTimer = null;
|
|
1636
|
-
const attemptExit = await new Promise((
|
|
2078
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1637
2079
|
let settled = false;
|
|
1638
2080
|
const settle = (result) => {
|
|
1639
2081
|
if (settled)
|
|
@@ -1641,7 +2083,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1641
2083
|
settled = true;
|
|
1642
2084
|
if (acceptedArtifactPollTimer)
|
|
1643
2085
|
clearInterval(acceptedArtifactPollTimer);
|
|
1644
|
-
|
|
2086
|
+
resolve7(result);
|
|
1645
2087
|
};
|
|
1646
2088
|
const pollAcceptedArtifacts = () => {
|
|
1647
2089
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1714,7 +2156,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1714
2156
|
});
|
|
1715
2157
|
for (const pendingLog of pendingLogs) {
|
|
1716
2158
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2159
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1717
2160
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2161
|
+
if (pendingLog.title === "Tool activity")
|
|
2162
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1718
2163
|
}
|
|
1719
2164
|
process.off("SIGTERM", forwardSigterm);
|
|
1720
2165
|
if (attemptExit.error) {
|
|
@@ -1840,8 +2285,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1840
2285
|
}
|
|
1841
2286
|
if (planningClassification.planningRequired) {
|
|
1842
2287
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1843
|
-
const expectedPlanPath =
|
|
1844
|
-
if (!
|
|
2288
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2289
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1845
2290
|
const failedAt = new Date().toISOString();
|
|
1846
2291
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1847
2292
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1861,6 +2306,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1861
2306
|
throw new CliError2(failureDetail, 1);
|
|
1862
2307
|
}
|
|
1863
2308
|
}
|
|
2309
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2310
|
+
appendPiStageLog({
|
|
2311
|
+
projectRoot: context.projectRoot,
|
|
2312
|
+
runId: input.runId,
|
|
2313
|
+
stage: "Validate",
|
|
2314
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2315
|
+
status: "completed"
|
|
2316
|
+
});
|
|
2317
|
+
if (verificationAction && !reviewStarted) {
|
|
2318
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2319
|
+
}
|
|
2320
|
+
if (!reviewAction) {
|
|
2321
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2322
|
+
}
|
|
2323
|
+
if (reviewAction) {
|
|
2324
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2325
|
+
}
|
|
2326
|
+
const requestedAt = new Date().toISOString();
|
|
2327
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2328
|
+
status: "reviewing",
|
|
2329
|
+
completedAt: null,
|
|
2330
|
+
errorText: null,
|
|
2331
|
+
serverCloseout: {
|
|
2332
|
+
status: "pending",
|
|
2333
|
+
phase: "queued",
|
|
2334
|
+
requestedAt,
|
|
2335
|
+
updatedAt: requestedAt,
|
|
2336
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2337
|
+
branch: latestRuntimeBranch,
|
|
2338
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2339
|
+
}
|
|
2340
|
+
});
|
|
2341
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2342
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2343
|
+
title: "Server-owned closeout requested",
|
|
2344
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2345
|
+
tone: "info",
|
|
2346
|
+
status: "reviewing",
|
|
2347
|
+
createdAt: requestedAt,
|
|
2348
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2349
|
+
});
|
|
2350
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2351
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2352
|
+
await context.emitEvent("command.finished", {
|
|
2353
|
+
command: [
|
|
2354
|
+
"rig",
|
|
2355
|
+
"server",
|
|
2356
|
+
"task-run",
|
|
2357
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2358
|
+
...input.title ? ["--title", input.title] : []
|
|
2359
|
+
],
|
|
2360
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2361
|
+
exitCode: 0,
|
|
2362
|
+
durationMs: 0,
|
|
2363
|
+
startedAt,
|
|
2364
|
+
finishedAt: requestedAt
|
|
2365
|
+
});
|
|
2366
|
+
process.exit(0);
|
|
2367
|
+
}
|
|
1864
2368
|
const runPiPrFeedbackFix = async (message) => {
|
|
1865
2369
|
appendPiStageLog({
|
|
1866
2370
|
projectRoot: context.projectRoot,
|
|
@@ -1888,11 +2392,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1888
2392
|
child.stdin.write(message);
|
|
1889
2393
|
}
|
|
1890
2394
|
child.stdin.end();
|
|
2395
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2396
|
+
let feedbackAssistantText = "";
|
|
2397
|
+
const feedbackPendingToolUses = new Map;
|
|
1891
2398
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1892
2399
|
stdout.on("line", (line) => {
|
|
1893
2400
|
const trimmed = line.trim();
|
|
1894
2401
|
if (!trimmed)
|
|
1895
2402
|
return;
|
|
2403
|
+
try {
|
|
2404
|
+
const record = JSON.parse(trimmed);
|
|
2405
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2406
|
+
runId: input.runId,
|
|
2407
|
+
record,
|
|
2408
|
+
createdAtFallback: new Date().toISOString(),
|
|
2409
|
+
status: "reviewing",
|
|
2410
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2411
|
+
});
|
|
2412
|
+
for (const providerLog of providerLogs) {
|
|
2413
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2414
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2415
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2416
|
+
if (providerLog.title === "Tool activity")
|
|
2417
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2418
|
+
}
|
|
2419
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2420
|
+
projectRoot: context.projectRoot,
|
|
2421
|
+
runId: input.runId,
|
|
2422
|
+
messageId: feedbackAssistantMessageId,
|
|
2423
|
+
record,
|
|
2424
|
+
assistantText: feedbackAssistantText
|
|
2425
|
+
});
|
|
2426
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2427
|
+
if (hadAssistantDelta) {
|
|
2428
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2429
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2430
|
+
}
|
|
2431
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2432
|
+
return;
|
|
2433
|
+
} catch {}
|
|
1896
2434
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1897
2435
|
id: nextRunLogId(),
|
|
1898
2436
|
title: "Pi PR feedback fix output",
|
|
@@ -1918,10 +2456,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1918
2456
|
});
|
|
1919
2457
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1920
2458
|
});
|
|
1921
|
-
const exitCode = await new Promise((
|
|
1922
|
-
child.once("error", () =>
|
|
1923
|
-
child.once("close", (code) =>
|
|
2459
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2460
|
+
child.once("error", () => resolve7(1));
|
|
2461
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1924
2462
|
});
|
|
2463
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2464
|
+
runId: input.runId,
|
|
2465
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2466
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2467
|
+
})) {
|
|
2468
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2469
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2470
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2471
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2472
|
+
}
|
|
2473
|
+
if (feedbackAssistantText.trim()) {
|
|
2474
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2475
|
+
id: feedbackAssistantMessageId,
|
|
2476
|
+
type: "assistant_message",
|
|
2477
|
+
text: feedbackAssistantText,
|
|
2478
|
+
state: "completed",
|
|
2479
|
+
createdAt: new Date().toISOString(),
|
|
2480
|
+
completedAt: new Date().toISOString()
|
|
2481
|
+
});
|
|
2482
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2483
|
+
}
|
|
1925
2484
|
if (exitCode !== 0) {
|
|
1926
2485
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1927
2486
|
}
|