@h-rig/cli 0.0.6-alpha.5 → 0.0.6-alpha.50
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 +4072 -1204
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +12 -6
- package/dist/src/commands/_doctor-checks.js +65 -34
- 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 +1109 -63
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +1066 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +757 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +820 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +84 -116
- package/dist/src/commands/_run-driver-helpers.js +2 -2
- package/dist/src/commands/_server-client.js +211 -48
- package/dist/src/commands/_snapshot-upload.js +60 -30
- package/dist/src/commands/_spinner.js +63 -0
- 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 +134 -26
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +65 -34
- package/dist/src/commands/github.js +62 -32
- package/dist/src/commands/inbox.js +396 -31
- package/dist/src/commands/init.js +342 -88
- package/dist/src/commands/inspect.js +282 -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 +1407 -130
- package/dist/src/commands/server.js +266 -40
- package/dist/src/commands/setup.js +69 -44
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +662 -70
- package/dist/src/commands/task.js +1846 -259
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +4054 -1180
- package/dist/src/index.js +4065 -1200
- 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 +7 -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,239 @@ 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 writeJsonFile3(path, value) {
|
|
422
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
423
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
424
|
+
`, "utf8");
|
|
425
|
+
}
|
|
426
|
+
function normalizeConnection(value) {
|
|
427
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
428
|
+
return null;
|
|
429
|
+
const record = value;
|
|
430
|
+
if (record.kind === "local")
|
|
431
|
+
return { kind: "local", mode: "auto" };
|
|
432
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
433
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
434
|
+
return { kind: "remote", baseUrl };
|
|
435
|
+
}
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
function readGlobalConnections(options = {}) {
|
|
439
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
440
|
+
const payload = readJsonFile(path);
|
|
441
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
442
|
+
return { connections: {} };
|
|
443
|
+
}
|
|
444
|
+
const rawConnections = payload.connections;
|
|
445
|
+
const connections = {};
|
|
446
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
447
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
448
|
+
const connection = normalizeConnection(raw);
|
|
449
|
+
if (connection)
|
|
450
|
+
connections[alias] = connection;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return { connections };
|
|
454
|
+
}
|
|
455
|
+
function readRepoConnection(projectRoot) {
|
|
456
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
457
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
458
|
+
return null;
|
|
459
|
+
const record = payload;
|
|
460
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
461
|
+
if (!selected)
|
|
462
|
+
return null;
|
|
463
|
+
return {
|
|
464
|
+
selected,
|
|
465
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
466
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
467
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function writeRepoConnection(projectRoot, state) {
|
|
471
|
+
writeJsonFile3(resolveRepoConnectionPath(projectRoot), state);
|
|
472
|
+
}
|
|
473
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
474
|
+
const repo = readRepoConnection(projectRoot);
|
|
475
|
+
if (!repo)
|
|
476
|
+
return null;
|
|
477
|
+
if (repo.selected === "local")
|
|
478
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
479
|
+
const global = readGlobalConnections(options);
|
|
480
|
+
const connection = global.connections[repo.selected];
|
|
481
|
+
if (!connection) {
|
|
482
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
483
|
+
}
|
|
484
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
485
|
+
}
|
|
486
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
487
|
+
const repo = readRepoConnection(projectRoot);
|
|
488
|
+
if (!repo)
|
|
489
|
+
return;
|
|
490
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// packages/cli/src/commands/_server-client.ts
|
|
494
|
+
var scopedGitHubBearerTokens = new Map;
|
|
495
|
+
function cleanToken(value) {
|
|
496
|
+
const trimmed = value?.trim();
|
|
497
|
+
return trimmed ? trimmed : null;
|
|
498
|
+
}
|
|
499
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
500
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
501
|
+
if (!existsSync3(path))
|
|
502
|
+
return null;
|
|
503
|
+
try {
|
|
504
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
505
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
506
|
+
} catch {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
511
|
+
const scopedKey = resolve5(projectRoot);
|
|
512
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
513
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
514
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
515
|
+
if (privateSession)
|
|
516
|
+
return privateSession;
|
|
517
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
518
|
+
}
|
|
519
|
+
async function ensureServerForCli(projectRoot) {
|
|
520
|
+
try {
|
|
521
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
522
|
+
if (selected?.connection.kind === "remote") {
|
|
523
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
524
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
525
|
+
return {
|
|
526
|
+
baseUrl: selected.connection.baseUrl,
|
|
527
|
+
authToken,
|
|
528
|
+
connectionKind: "remote",
|
|
529
|
+
serverProjectRoot
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
533
|
+
return {
|
|
534
|
+
baseUrl: connection.baseUrl,
|
|
535
|
+
authToken: connection.authToken,
|
|
536
|
+
connectionKind: "local",
|
|
537
|
+
serverProjectRoot: resolve5(projectRoot)
|
|
538
|
+
};
|
|
539
|
+
} catch (error) {
|
|
540
|
+
if (error instanceof Error) {
|
|
541
|
+
throw new CliError2(error.message, 1);
|
|
542
|
+
}
|
|
543
|
+
throw error;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
547
|
+
const repo = readRepoConnection(projectRoot);
|
|
548
|
+
const slug = repo?.project?.trim();
|
|
549
|
+
if (!slug)
|
|
550
|
+
return null;
|
|
551
|
+
try {
|
|
552
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
553
|
+
headers: mergeHeaders(undefined, authToken)
|
|
554
|
+
});
|
|
555
|
+
if (!response.ok)
|
|
556
|
+
return null;
|
|
557
|
+
const payload = await response.json();
|
|
558
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
559
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
560
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
561
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
562
|
+
if (path)
|
|
563
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
564
|
+
return path;
|
|
565
|
+
} catch {
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function mergeHeaders(headers, authToken) {
|
|
570
|
+
const merged = new Headers(headers);
|
|
571
|
+
if (authToken) {
|
|
572
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
573
|
+
}
|
|
574
|
+
return merged;
|
|
575
|
+
}
|
|
576
|
+
function diagnosticMessage(payload) {
|
|
577
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
578
|
+
return null;
|
|
579
|
+
const record = payload;
|
|
580
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
581
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
582
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
583
|
+
return [];
|
|
584
|
+
const diagnostic = entry;
|
|
585
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
586
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
587
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
588
|
+
});
|
|
589
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
590
|
+
}
|
|
591
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
592
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
593
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
594
|
+
if (server.serverProjectRoot)
|
|
595
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
596
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
597
|
+
...init,
|
|
598
|
+
headers
|
|
599
|
+
});
|
|
600
|
+
const text = await response.text();
|
|
601
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
602
|
+
try {
|
|
603
|
+
return JSON.parse(text);
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
})() : null;
|
|
608
|
+
if (!response.ok) {
|
|
609
|
+
const diagnostics = diagnosticMessage(payload);
|
|
610
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
611
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
612
|
+
}
|
|
613
|
+
return payload;
|
|
614
|
+
}
|
|
615
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
616
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
617
|
+
method: "POST",
|
|
618
|
+
headers: { "content-type": "application/json" },
|
|
619
|
+
body: JSON.stringify(input)
|
|
620
|
+
});
|
|
621
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
622
|
+
}
|
|
623
|
+
|
|
391
624
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
625
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
626
|
"Connect",
|
|
@@ -431,6 +664,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
664
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
665
|
RIG_TASK_ID: input.taskId,
|
|
433
666
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
667
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
668
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
669
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
670
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +689,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
689
|
return 0;
|
|
456
690
|
let copied = 0;
|
|
457
691
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
692
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
693
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
694
|
try {
|
|
461
695
|
if (!statSync(sourcePath).isFile())
|
|
462
696
|
continue;
|
|
463
|
-
|
|
697
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
698
|
copyFileSync(sourcePath, targetPath);
|
|
465
699
|
copied += 1;
|
|
466
700
|
} catch {}
|
|
@@ -499,7 +733,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
733
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
734
|
return {
|
|
501
735
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
736
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
737
|
};
|
|
504
738
|
}
|
|
505
739
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +741,7 @@ function positiveInt(value, fallback) {
|
|
|
507
741
|
}
|
|
508
742
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
743
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
744
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
745
|
return {
|
|
512
746
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
747
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +835,6 @@ function appendPiStageLog(input) {
|
|
|
601
835
|
});
|
|
602
836
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
837
|
}
|
|
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
838
|
function createCommandRunner(binary) {
|
|
611
839
|
return async (args, options) => {
|
|
612
840
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +845,9 @@ function createCommandRunner(binary) {
|
|
|
617
845
|
const stderrChunks = [];
|
|
618
846
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
847
|
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) =>
|
|
848
|
+
return await new Promise((resolve7) => {
|
|
849
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
850
|
+
child.once("close", (code) => resolve7({
|
|
623
851
|
exitCode: code ?? 1,
|
|
624
852
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
853
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +871,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
871
|
if (!taskId) {
|
|
644
872
|
return { status: "skipped" };
|
|
645
873
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
874
|
+
const configInput = input.config ?? null;
|
|
875
|
+
const config = configInput ?? {};
|
|
876
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
877
|
if (prMode === "off" || prMode === "ask") {
|
|
649
878
|
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
879
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +889,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
889
|
gitCommand
|
|
661
890
|
});
|
|
662
891
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
892
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
893
|
const stage = input.appendStage ?? (() => {
|
|
665
894
|
return;
|
|
666
895
|
});
|
|
@@ -682,7 +911,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
911
|
command: gitCommand
|
|
683
912
|
});
|
|
684
913
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
914
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
915
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
916
|
const pr = await prAutomation({
|
|
688
917
|
projectRoot: workspace,
|
|
@@ -692,7 +921,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
921
|
config,
|
|
693
922
|
sourceTask: input.sourceTask,
|
|
694
923
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
924
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
925
|
command: ghCommand,
|
|
926
|
+
gitCommand,
|
|
696
927
|
steerPi,
|
|
697
928
|
lifecycle: {
|
|
698
929
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +972,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
972
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
973
|
})
|
|
743
974
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
975
|
});
|
|
747
976
|
}
|
|
748
977
|
},
|
|
@@ -761,8 +990,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
990
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
991
|
})
|
|
763
992
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
993
|
});
|
|
767
994
|
}
|
|
768
995
|
},
|
|
@@ -791,8 +1018,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
1018
|
errorText: detail
|
|
792
1019
|
})
|
|
793
1020
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
1021
|
+
}).catch((error) => {
|
|
1022
|
+
try {
|
|
1023
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1024
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
1025
|
+
title: "Task source needs-attention update failed",
|
|
1026
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1027
|
+
tone: "error",
|
|
1028
|
+
status: "needs_attention",
|
|
1029
|
+
createdAt: new Date().toISOString()
|
|
1030
|
+
});
|
|
1031
|
+
} catch {}
|
|
796
1032
|
});
|
|
797
1033
|
}
|
|
798
1034
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1051,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1051
|
return null;
|
|
816
1052
|
}
|
|
817
1053
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1054
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1055
|
if (!summary || summary.status !== "fail") {
|
|
820
1056
|
continue;
|
|
821
1057
|
}
|
|
@@ -896,14 +1132,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1132
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1133
|
return { accepted: false, reason: null };
|
|
898
1134
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1135
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1136
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1137
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1138
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1139
|
if (reviewStatus !== "APPROVED") {
|
|
904
1140
|
return { accepted: false, reason: null };
|
|
905
1141
|
}
|
|
906
|
-
const taskResult =
|
|
1142
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1143
|
if (taskResult?.status !== "completed") {
|
|
908
1144
|
return { accepted: false, reason: null };
|
|
909
1145
|
}
|
|
@@ -935,13 +1171,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1171
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1172
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1173
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1174
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1175
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1176
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1177
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1178
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1179
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1180
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1181
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1182
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1183
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1230,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1230
|
}
|
|
995
1231
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1232
|
}
|
|
1233
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1234
|
+
let nextAssistantText = input.assistantText;
|
|
1235
|
+
if (input.record.type === "message_update") {
|
|
1236
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1237
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1238
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1239
|
+
}
|
|
1240
|
+
} else if (input.record.type === "stream_event") {
|
|
1241
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1242
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1243
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1244
|
+
nextAssistantText += delta.text;
|
|
1245
|
+
}
|
|
1246
|
+
} else if (input.record.type === "assistant") {
|
|
1247
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1248
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1249
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1250
|
+
if (fullText.length > nextAssistantText.length)
|
|
1251
|
+
nextAssistantText = fullText;
|
|
1252
|
+
}
|
|
1253
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1254
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1255
|
+
id: input.messageId,
|
|
1256
|
+
type: "assistant_message",
|
|
1257
|
+
text: nextAssistantText,
|
|
1258
|
+
state: "streaming",
|
|
1259
|
+
createdAt: new Date().toISOString()
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
return nextAssistantText;
|
|
1263
|
+
}
|
|
1264
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1265
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1266
|
+
if (type === "response") {
|
|
1267
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1268
|
+
const success = input.record.success !== false;
|
|
1269
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1273
|
+
id: input.nextRunLogId(),
|
|
1274
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1275
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1276
|
+
tone: success ? "tool" : "error",
|
|
1277
|
+
status: input.status,
|
|
1278
|
+
payload: input.record,
|
|
1279
|
+
createdAt: new Date().toISOString()
|
|
1280
|
+
});
|
|
1281
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1282
|
+
return true;
|
|
1283
|
+
}
|
|
1284
|
+
if (type !== "extension_ui_request")
|
|
1285
|
+
return false;
|
|
1286
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1287
|
+
let title = "Pi UI event";
|
|
1288
|
+
let detail = method;
|
|
1289
|
+
let tone = "info";
|
|
1290
|
+
if (method === "notify") {
|
|
1291
|
+
title = "Pi notification";
|
|
1292
|
+
detail = String(input.record.message ?? "");
|
|
1293
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1294
|
+
} else if (method === "setStatus") {
|
|
1295
|
+
title = "Pi UI status";
|
|
1296
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1297
|
+
tone = "tool";
|
|
1298
|
+
} else if (method === "setWidget") {
|
|
1299
|
+
title = "Pi UI widget";
|
|
1300
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1301
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1302
|
+
tone = "tool";
|
|
1303
|
+
} else if (method === "setTitle") {
|
|
1304
|
+
title = "Pi UI title";
|
|
1305
|
+
detail = String(input.record.title ?? "");
|
|
1306
|
+
tone = "tool";
|
|
1307
|
+
} else if (method === "set_editor_text") {
|
|
1308
|
+
title = "Pi editor update";
|
|
1309
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1310
|
+
tone = "tool";
|
|
1311
|
+
} else {
|
|
1312
|
+
title = "Pi UI request";
|
|
1313
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1314
|
+
}
|
|
1315
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1316
|
+
id: input.nextRunLogId(),
|
|
1317
|
+
title,
|
|
1318
|
+
detail,
|
|
1319
|
+
tone,
|
|
1320
|
+
status: input.status,
|
|
1321
|
+
payload: input.record,
|
|
1322
|
+
createdAt: new Date().toISOString()
|
|
1323
|
+
});
|
|
1324
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1325
|
+
return true;
|
|
1326
|
+
}
|
|
1327
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1328
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1329
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1330
|
+
return false;
|
|
1331
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1332
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1333
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1334
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1335
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1336
|
+
type,
|
|
1337
|
+
toolName,
|
|
1338
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1339
|
+
createdAt: new Date().toISOString()
|
|
1340
|
+
});
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1344
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1345
|
+
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");
|
|
1346
|
+
}
|
|
1347
|
+
function appendToolTimelineFromLog(input) {
|
|
1348
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1349
|
+
if (title !== "Tool activity")
|
|
1350
|
+
return;
|
|
1351
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1352
|
+
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;
|
|
1353
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1354
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1355
|
+
id: `tool:${logId}`,
|
|
1356
|
+
type: "tool_execution_update",
|
|
1357
|
+
toolName,
|
|
1358
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1359
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1360
|
+
payload,
|
|
1361
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
997
1364
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1365
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1366
|
return null;
|
|
1000
1367
|
}
|
|
1001
1368
|
try {
|
|
1002
|
-
const status =
|
|
1369
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1370
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1371
|
} catch {
|
|
1005
1372
|
return null;
|
|
@@ -1017,7 +1384,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1384
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1385
|
return sourceTask;
|
|
1019
1386
|
}
|
|
1020
|
-
|
|
1387
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1388
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1389
|
+
return null;
|
|
1390
|
+
const record = sourceTask;
|
|
1391
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1392
|
+
if (direct?.trim())
|
|
1393
|
+
return direct.trim();
|
|
1394
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1395
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1396
|
+
}
|
|
1397
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1398
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1399
|
+
id: input.taskId,
|
|
1400
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1401
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1402
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1403
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1404
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1405
|
+
});
|
|
1406
|
+
if (serverResult.ok === false) {
|
|
1407
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1408
|
+
}
|
|
1409
|
+
return {
|
|
1410
|
+
updated: serverResult.ok !== false,
|
|
1411
|
+
taskId: input.taskId,
|
|
1412
|
+
status: input.update.status,
|
|
1413
|
+
source: "server",
|
|
1414
|
+
sourceKind: "server",
|
|
1415
|
+
projectSync: serverResult.projectSync
|
|
1416
|
+
};
|
|
1417
|
+
};
|
|
1418
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1419
|
if (!taskId)
|
|
1022
1420
|
return;
|
|
1023
1421
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1107,11 +1505,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1107
1505
|
...input.model ? ["--model", input.model] : [],
|
|
1108
1506
|
"--prompt"
|
|
1109
1507
|
] : input.runtimeAdapter === "pi" ? [
|
|
1110
|
-
"
|
|
1111
|
-
"--verbose",
|
|
1112
|
-
"--mode",
|
|
1113
|
-
"json",
|
|
1114
|
-
"--no-session",
|
|
1508
|
+
"__rig_pi_session_daemon__",
|
|
1115
1509
|
...input.model ? ["--model", input.model] : []
|
|
1116
1510
|
] : [
|
|
1117
1511
|
"--print",
|
|
@@ -1186,15 +1580,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1186
1580
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1187
1581
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1188
1582
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1189
|
-
const planningArtifactPath =
|
|
1583
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1190
1584
|
const persistedPlanning = {
|
|
1191
1585
|
...planningClassification,
|
|
1192
1586
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1193
1587
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1194
1588
|
classifiedAt: new Date().toISOString()
|
|
1195
1589
|
};
|
|
1196
|
-
|
|
1197
|
-
|
|
1590
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1591
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1198
1592
|
`, "utf8");
|
|
1199
1593
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1200
1594
|
prompt = `${prompt}
|
|
@@ -1208,7 +1602,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1208
1602
|
projectRoot: context.projectRoot,
|
|
1209
1603
|
runId: input.runId,
|
|
1210
1604
|
stage,
|
|
1211
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1605
|
+
detail: stage === "Launch Pi" ? "Worker Pi SDK session daemon starting; local frontend will attach over Rig-proxied WebSocket." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running in the worker runtime." : null,
|
|
1212
1606
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1213
1607
|
});
|
|
1214
1608
|
}
|
|
@@ -1244,11 +1638,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1244
1638
|
let verificationStarted = false;
|
|
1245
1639
|
let reviewStarted = false;
|
|
1246
1640
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1247
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1641
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1248
1642
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1249
1643
|
let latestProviderCommand = null;
|
|
1250
1644
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1251
1645
|
let snapshotSidecarPromise = null;
|
|
1646
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1252
1647
|
let dirtyBaselineApplied = false;
|
|
1253
1648
|
const childEnv = {
|
|
1254
1649
|
...process.env,
|
|
@@ -1301,6 +1696,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1301
1696
|
detail: detail ?? "Verifier review is running."
|
|
1302
1697
|
});
|
|
1303
1698
|
};
|
|
1699
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1304
1700
|
const handleWrapperEvent = (rawPayload) => {
|
|
1305
1701
|
let event = null;
|
|
1306
1702
|
try {
|
|
@@ -1316,6 +1712,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1316
1712
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1317
1713
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1318
1714
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1715
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1319
1716
|
latestRuntimeBranch = runtimeId;
|
|
1320
1717
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1321
1718
|
runtimeId: runtimeId ?? null,
|
|
@@ -1327,10 +1724,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1327
1724
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1328
1725
|
status: "running",
|
|
1329
1726
|
worktreePath: latestRuntimeWorkspace,
|
|
1330
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1727
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1331
1728
|
logRoot: latestLogsDir,
|
|
1332
|
-
sessionPath: latestSessionDir ?
|
|
1333
|
-
sessionLogPath: latestLogsDir ?
|
|
1729
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1730
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1334
1731
|
branch: runtimeId
|
|
1335
1732
|
});
|
|
1336
1733
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1338,8 +1735,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1338
1735
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1339
1736
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1340
1737
|
if (readyFile) {
|
|
1341
|
-
|
|
1342
|
-
|
|
1738
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1739
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1343
1740
|
`, "utf8");
|
|
1344
1741
|
}
|
|
1345
1742
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1353,7 +1750,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1353
1750
|
});
|
|
1354
1751
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1355
1752
|
}
|
|
1356
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1753
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1357
1754
|
snapshotSidecarPromise = (async () => {
|
|
1358
1755
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1359
1756
|
projectRoot: context.projectRoot,
|
|
@@ -1435,9 +1832,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1435
1832
|
}
|
|
1436
1833
|
return true;
|
|
1437
1834
|
}
|
|
1835
|
+
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") {
|
|
1836
|
+
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";
|
|
1837
|
+
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");
|
|
1838
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1839
|
+
id: nextRunLogId(),
|
|
1840
|
+
title,
|
|
1841
|
+
detail,
|
|
1842
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1843
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1844
|
+
payload: {
|
|
1845
|
+
eventType: event.type,
|
|
1846
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1847
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1848
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1849
|
+
},
|
|
1850
|
+
createdAt: new Date().toISOString()
|
|
1851
|
+
});
|
|
1852
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1853
|
+
return true;
|
|
1854
|
+
}
|
|
1855
|
+
if (event.type === "pi.session.ready") {
|
|
1856
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1857
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1858
|
+
if (privateMetadata) {
|
|
1859
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1860
|
+
piSession: publicMetadata,
|
|
1861
|
+
piSessionPrivate: privateMetadata
|
|
1862
|
+
});
|
|
1863
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1864
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1865
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1866
|
+
title: "Worker Pi session ready",
|
|
1867
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1868
|
+
tone: "info",
|
|
1869
|
+
status: "running",
|
|
1870
|
+
payload: {
|
|
1871
|
+
sessionId: sessionId || null,
|
|
1872
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1873
|
+
},
|
|
1874
|
+
createdAt: new Date().toISOString()
|
|
1875
|
+
});
|
|
1876
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1877
|
+
}
|
|
1878
|
+
return true;
|
|
1879
|
+
}
|
|
1880
|
+
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") {
|
|
1881
|
+
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";
|
|
1882
|
+
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")}`;
|
|
1883
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1884
|
+
id: nextRunLogId(),
|
|
1885
|
+
title,
|
|
1886
|
+
detail,
|
|
1887
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1888
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1889
|
+
payload,
|
|
1890
|
+
createdAt: new Date().toISOString()
|
|
1891
|
+
});
|
|
1892
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1893
|
+
return true;
|
|
1894
|
+
}
|
|
1438
1895
|
return false;
|
|
1439
1896
|
};
|
|
1440
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1441
1897
|
const handleAgentStdoutLine = (line) => {
|
|
1442
1898
|
const trimmed = line.trim();
|
|
1443
1899
|
if (!trimmed)
|
|
@@ -1471,6 +1927,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1471
1927
|
try {
|
|
1472
1928
|
const record = JSON.parse(trimmed);
|
|
1473
1929
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1930
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1931
|
+
projectRoot: context.projectRoot,
|
|
1932
|
+
runId: input.runId,
|
|
1933
|
+
record,
|
|
1934
|
+
status: liveLogStatus,
|
|
1935
|
+
nextRunLogId
|
|
1936
|
+
})) {
|
|
1937
|
+
return;
|
|
1938
|
+
}
|
|
1939
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1940
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1474
1943
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1475
1944
|
runId: input.runId,
|
|
1476
1945
|
record,
|
|
@@ -1487,7 +1956,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1487
1956
|
if (providerLogs.length > 0) {
|
|
1488
1957
|
for (const providerLog of providerLogs) {
|
|
1489
1958
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1959
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1490
1960
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1961
|
+
if (providerLog.title === "Tool activity")
|
|
1962
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1491
1963
|
}
|
|
1492
1964
|
}
|
|
1493
1965
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1539,6 +2011,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1539
2011
|
return;
|
|
1540
2012
|
}
|
|
1541
2013
|
}
|
|
2014
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
1542
2017
|
if (record.type === "assistant") {
|
|
1543
2018
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1544
2019
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1645,7 +2120,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1645
2120
|
let acceptedArtifactObservedAt = null;
|
|
1646
2121
|
let acceptedArtifactPollTimer = null;
|
|
1647
2122
|
let acceptedArtifactKillTimer = null;
|
|
1648
|
-
const attemptExit = await new Promise((
|
|
2123
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1649
2124
|
let settled = false;
|
|
1650
2125
|
const settle = (result) => {
|
|
1651
2126
|
if (settled)
|
|
@@ -1653,7 +2128,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1653
2128
|
settled = true;
|
|
1654
2129
|
if (acceptedArtifactPollTimer)
|
|
1655
2130
|
clearInterval(acceptedArtifactPollTimer);
|
|
1656
|
-
|
|
2131
|
+
resolve7(result);
|
|
1657
2132
|
};
|
|
1658
2133
|
const pollAcceptedArtifacts = () => {
|
|
1659
2134
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1726,7 +2201,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1726
2201
|
});
|
|
1727
2202
|
for (const pendingLog of pendingLogs) {
|
|
1728
2203
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2204
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1729
2205
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2206
|
+
if (pendingLog.title === "Tool activity")
|
|
2207
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1730
2208
|
}
|
|
1731
2209
|
process.off("SIGTERM", forwardSigterm);
|
|
1732
2210
|
if (attemptExit.error) {
|
|
@@ -1852,8 +2330,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1852
2330
|
}
|
|
1853
2331
|
if (planningClassification.planningRequired) {
|
|
1854
2332
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1855
|
-
const expectedPlanPath =
|
|
1856
|
-
if (!
|
|
2333
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2334
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1857
2335
|
const failedAt = new Date().toISOString();
|
|
1858
2336
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1859
2337
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1873,6 +2351,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1873
2351
|
throw new CliError2(failureDetail, 1);
|
|
1874
2352
|
}
|
|
1875
2353
|
}
|
|
2354
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2355
|
+
appendPiStageLog({
|
|
2356
|
+
projectRoot: context.projectRoot,
|
|
2357
|
+
runId: input.runId,
|
|
2358
|
+
stage: "Validate",
|
|
2359
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2360
|
+
status: "completed"
|
|
2361
|
+
});
|
|
2362
|
+
if (verificationAction && !reviewStarted) {
|
|
2363
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2364
|
+
}
|
|
2365
|
+
if (!reviewAction) {
|
|
2366
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2367
|
+
}
|
|
2368
|
+
if (reviewAction) {
|
|
2369
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2370
|
+
}
|
|
2371
|
+
const requestedAt = new Date().toISOString();
|
|
2372
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2373
|
+
status: "reviewing",
|
|
2374
|
+
completedAt: null,
|
|
2375
|
+
errorText: null,
|
|
2376
|
+
serverCloseout: {
|
|
2377
|
+
status: "pending",
|
|
2378
|
+
phase: "queued",
|
|
2379
|
+
requestedAt,
|
|
2380
|
+
updatedAt: requestedAt,
|
|
2381
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2382
|
+
branch: latestRuntimeBranch,
|
|
2383
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2384
|
+
}
|
|
2385
|
+
});
|
|
2386
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2387
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2388
|
+
title: "Server-owned closeout requested",
|
|
2389
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2390
|
+
tone: "info",
|
|
2391
|
+
status: "reviewing",
|
|
2392
|
+
createdAt: requestedAt,
|
|
2393
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2394
|
+
});
|
|
2395
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2396
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2397
|
+
await context.emitEvent("command.finished", {
|
|
2398
|
+
command: [
|
|
2399
|
+
"rig",
|
|
2400
|
+
"server",
|
|
2401
|
+
"task-run",
|
|
2402
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2403
|
+
...input.title ? ["--title", input.title] : []
|
|
2404
|
+
],
|
|
2405
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2406
|
+
exitCode: 0,
|
|
2407
|
+
durationMs: 0,
|
|
2408
|
+
startedAt,
|
|
2409
|
+
finishedAt: requestedAt
|
|
2410
|
+
});
|
|
2411
|
+
process.exit(0);
|
|
2412
|
+
}
|
|
1876
2413
|
const runPiPrFeedbackFix = async (message) => {
|
|
1877
2414
|
appendPiStageLog({
|
|
1878
2415
|
projectRoot: context.projectRoot,
|
|
@@ -1900,11 +2437,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1900
2437
|
child.stdin.write(message);
|
|
1901
2438
|
}
|
|
1902
2439
|
child.stdin.end();
|
|
2440
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2441
|
+
let feedbackAssistantText = "";
|
|
2442
|
+
const feedbackPendingToolUses = new Map;
|
|
1903
2443
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1904
2444
|
stdout.on("line", (line) => {
|
|
1905
2445
|
const trimmed = line.trim();
|
|
1906
2446
|
if (!trimmed)
|
|
1907
2447
|
return;
|
|
2448
|
+
try {
|
|
2449
|
+
const record = JSON.parse(trimmed);
|
|
2450
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2451
|
+
runId: input.runId,
|
|
2452
|
+
record,
|
|
2453
|
+
createdAtFallback: new Date().toISOString(),
|
|
2454
|
+
status: "reviewing",
|
|
2455
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2456
|
+
});
|
|
2457
|
+
for (const providerLog of providerLogs) {
|
|
2458
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2459
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2460
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2461
|
+
if (providerLog.title === "Tool activity")
|
|
2462
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2463
|
+
}
|
|
2464
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2465
|
+
projectRoot: context.projectRoot,
|
|
2466
|
+
runId: input.runId,
|
|
2467
|
+
messageId: feedbackAssistantMessageId,
|
|
2468
|
+
record,
|
|
2469
|
+
assistantText: feedbackAssistantText
|
|
2470
|
+
});
|
|
2471
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2472
|
+
if (hadAssistantDelta) {
|
|
2473
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2474
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2475
|
+
}
|
|
2476
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2477
|
+
return;
|
|
2478
|
+
} catch {}
|
|
1908
2479
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1909
2480
|
id: nextRunLogId(),
|
|
1910
2481
|
title: "Pi PR feedback fix output",
|
|
@@ -1930,10 +2501,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1930
2501
|
});
|
|
1931
2502
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1932
2503
|
});
|
|
1933
|
-
const exitCode = await new Promise((
|
|
1934
|
-
child.once("error", () =>
|
|
1935
|
-
child.once("close", (code) =>
|
|
2504
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2505
|
+
child.once("error", () => resolve7(1));
|
|
2506
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1936
2507
|
});
|
|
2508
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2509
|
+
runId: input.runId,
|
|
2510
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2511
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2512
|
+
})) {
|
|
2513
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2514
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2515
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2516
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2517
|
+
}
|
|
2518
|
+
if (feedbackAssistantText.trim()) {
|
|
2519
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2520
|
+
id: feedbackAssistantMessageId,
|
|
2521
|
+
type: "assistant_message",
|
|
2522
|
+
text: feedbackAssistantText,
|
|
2523
|
+
state: "completed",
|
|
2524
|
+
createdAt: new Date().toISOString(),
|
|
2525
|
+
completedAt: new Date().toISOString()
|
|
2526
|
+
});
|
|
2527
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2528
|
+
}
|
|
1937
2529
|
if (exitCode !== 0) {
|
|
1938
2530
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1939
2531
|
}
|