@h-rig/cli 0.0.6-alpha.5 → 0.0.6-alpha.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/rig.js +4129 -1207
- 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 +79 -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 +1124 -64
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +1080 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-remote-session.js +771 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +834 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +98 -116
- package/dist/src/commands/_run-driver-helpers.js +2 -2
- package/dist/src/commands/_server-client.js +225 -48
- package/dist/src/commands/_snapshot-upload.js +74 -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 +79 -34
- package/dist/src/commands/github.js +76 -32
- package/dist/src/commands/inbox.js +410 -31
- package/dist/src/commands/init.js +398 -90
- package/dist/src/commands/inspect.js +296 -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 +1422 -131
- package/dist/src/commands/server.js +280 -40
- package/dist/src/commands/setup.js +84 -45
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +676 -70
- package/dist/src/commands/task.js +1861 -260
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +4111 -1183
- package/dist/src/index.js +4122 -1203
- 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,253 @@ 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
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
520
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
521
|
+
if (!existsSync3(path))
|
|
522
|
+
return null;
|
|
523
|
+
try {
|
|
524
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
525
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
526
|
+
} catch {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
531
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
532
|
+
}
|
|
533
|
+
async function ensureServerForCli(projectRoot) {
|
|
534
|
+
try {
|
|
535
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
536
|
+
if (selected?.connection.kind === "remote") {
|
|
537
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
538
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
539
|
+
return {
|
|
540
|
+
baseUrl: selected.connection.baseUrl,
|
|
541
|
+
authToken,
|
|
542
|
+
connectionKind: "remote",
|
|
543
|
+
serverProjectRoot
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
547
|
+
return {
|
|
548
|
+
baseUrl: connection.baseUrl,
|
|
549
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
550
|
+
connectionKind: "local",
|
|
551
|
+
serverProjectRoot: resolve5(projectRoot)
|
|
552
|
+
};
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (error instanceof Error) {
|
|
555
|
+
throw new CliError2(error.message, 1);
|
|
556
|
+
}
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
561
|
+
const repo = readRepoConnection(projectRoot);
|
|
562
|
+
const slug = repo?.project?.trim();
|
|
563
|
+
if (!slug)
|
|
564
|
+
return null;
|
|
565
|
+
try {
|
|
566
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
567
|
+
headers: mergeHeaders(undefined, authToken)
|
|
568
|
+
});
|
|
569
|
+
if (!response.ok)
|
|
570
|
+
return null;
|
|
571
|
+
const payload = await response.json();
|
|
572
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
573
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
574
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
575
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
576
|
+
if (path)
|
|
577
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
578
|
+
return path;
|
|
579
|
+
} catch {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function mergeHeaders(headers, authToken) {
|
|
584
|
+
const merged = new Headers(headers);
|
|
585
|
+
if (authToken) {
|
|
586
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
587
|
+
}
|
|
588
|
+
return merged;
|
|
589
|
+
}
|
|
590
|
+
function diagnosticMessage(payload) {
|
|
591
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
592
|
+
return null;
|
|
593
|
+
const record = payload;
|
|
594
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
595
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
596
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
597
|
+
return [];
|
|
598
|
+
const diagnostic = entry;
|
|
599
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
600
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
601
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
602
|
+
});
|
|
603
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
604
|
+
}
|
|
605
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
606
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
607
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
608
|
+
if (server.serverProjectRoot)
|
|
609
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
610
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
611
|
+
...init,
|
|
612
|
+
headers
|
|
613
|
+
});
|
|
614
|
+
const text = await response.text();
|
|
615
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
616
|
+
try {
|
|
617
|
+
return JSON.parse(text);
|
|
618
|
+
} catch {
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
})() : null;
|
|
622
|
+
if (!response.ok) {
|
|
623
|
+
const diagnostics = diagnosticMessage(payload);
|
|
624
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
625
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
626
|
+
}
|
|
627
|
+
return payload;
|
|
628
|
+
}
|
|
629
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
630
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
631
|
+
method: "POST",
|
|
632
|
+
headers: { "content-type": "application/json" },
|
|
633
|
+
body: JSON.stringify(input)
|
|
634
|
+
});
|
|
635
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
636
|
+
}
|
|
637
|
+
|
|
391
638
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
639
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
640
|
"Connect",
|
|
@@ -431,6 +678,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
678
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
679
|
RIG_TASK_ID: input.taskId,
|
|
433
680
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
681
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
682
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
683
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
684
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +703,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
703
|
return 0;
|
|
456
704
|
let copied = 0;
|
|
457
705
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
706
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
707
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
708
|
try {
|
|
461
709
|
if (!statSync(sourcePath).isFile())
|
|
462
710
|
continue;
|
|
463
|
-
|
|
711
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
712
|
copyFileSync(sourcePath, targetPath);
|
|
465
713
|
copied += 1;
|
|
466
714
|
} catch {}
|
|
@@ -499,7 +747,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
747
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
748
|
return {
|
|
501
749
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
750
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
751
|
};
|
|
504
752
|
}
|
|
505
753
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +755,7 @@ function positiveInt(value, fallback) {
|
|
|
507
755
|
}
|
|
508
756
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
757
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
758
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
759
|
return {
|
|
512
760
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
761
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +849,6 @@ function appendPiStageLog(input) {
|
|
|
601
849
|
});
|
|
602
850
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
851
|
}
|
|
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
852
|
function createCommandRunner(binary) {
|
|
611
853
|
return async (args, options) => {
|
|
612
854
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +859,9 @@ function createCommandRunner(binary) {
|
|
|
617
859
|
const stderrChunks = [];
|
|
618
860
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
861
|
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) =>
|
|
862
|
+
return await new Promise((resolve7) => {
|
|
863
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
864
|
+
child.once("close", (code) => resolve7({
|
|
623
865
|
exitCode: code ?? 1,
|
|
624
866
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
867
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +885,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
885
|
if (!taskId) {
|
|
644
886
|
return { status: "skipped" };
|
|
645
887
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
888
|
+
const configInput = input.config ?? null;
|
|
889
|
+
const config = configInput ?? {};
|
|
890
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
891
|
if (prMode === "off" || prMode === "ask") {
|
|
649
892
|
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
893
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +903,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
903
|
gitCommand
|
|
661
904
|
});
|
|
662
905
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
906
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
907
|
const stage = input.appendStage ?? (() => {
|
|
665
908
|
return;
|
|
666
909
|
});
|
|
@@ -682,7 +925,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
925
|
command: gitCommand
|
|
683
926
|
});
|
|
684
927
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
928
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
929
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
930
|
const pr = await prAutomation({
|
|
688
931
|
projectRoot: workspace,
|
|
@@ -692,7 +935,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
935
|
config,
|
|
693
936
|
sourceTask: input.sourceTask,
|
|
694
937
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
938
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
939
|
command: ghCommand,
|
|
940
|
+
gitCommand,
|
|
696
941
|
steerPi,
|
|
697
942
|
lifecycle: {
|
|
698
943
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +986,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
986
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
987
|
})
|
|
743
988
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
989
|
});
|
|
747
990
|
}
|
|
748
991
|
},
|
|
@@ -761,8 +1004,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
1004
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
1005
|
})
|
|
763
1006
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
1007
|
});
|
|
767
1008
|
}
|
|
768
1009
|
},
|
|
@@ -791,8 +1032,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
1032
|
errorText: detail
|
|
792
1033
|
})
|
|
793
1034
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
1035
|
+
}).catch((error) => {
|
|
1036
|
+
try {
|
|
1037
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1038
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
1039
|
+
title: "Task source needs-attention update failed",
|
|
1040
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1041
|
+
tone: "error",
|
|
1042
|
+
status: "needs_attention",
|
|
1043
|
+
createdAt: new Date().toISOString()
|
|
1044
|
+
});
|
|
1045
|
+
} catch {}
|
|
796
1046
|
});
|
|
797
1047
|
}
|
|
798
1048
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1065,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1065
|
return null;
|
|
816
1066
|
}
|
|
817
1067
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1068
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1069
|
if (!summary || summary.status !== "fail") {
|
|
820
1070
|
continue;
|
|
821
1071
|
}
|
|
@@ -896,14 +1146,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1146
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1147
|
return { accepted: false, reason: null };
|
|
898
1148
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1149
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1150
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1151
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1152
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1153
|
if (reviewStatus !== "APPROVED") {
|
|
904
1154
|
return { accepted: false, reason: null };
|
|
905
1155
|
}
|
|
906
|
-
const taskResult =
|
|
1156
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1157
|
if (taskResult?.status !== "completed") {
|
|
908
1158
|
return { accepted: false, reason: null };
|
|
909
1159
|
}
|
|
@@ -935,13 +1185,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1185
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1186
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1187
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1188
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1189
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1190
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1191
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1192
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1193
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1194
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1195
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1196
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1197
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1244,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1244
|
}
|
|
995
1245
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1246
|
}
|
|
1247
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1248
|
+
let nextAssistantText = input.assistantText;
|
|
1249
|
+
if (input.record.type === "message_update") {
|
|
1250
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1251
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1252
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1253
|
+
}
|
|
1254
|
+
} else if (input.record.type === "stream_event") {
|
|
1255
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1256
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1257
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1258
|
+
nextAssistantText += delta.text;
|
|
1259
|
+
}
|
|
1260
|
+
} else if (input.record.type === "assistant") {
|
|
1261
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1262
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1263
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1264
|
+
if (fullText.length > nextAssistantText.length)
|
|
1265
|
+
nextAssistantText = fullText;
|
|
1266
|
+
}
|
|
1267
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1268
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1269
|
+
id: input.messageId,
|
|
1270
|
+
type: "assistant_message",
|
|
1271
|
+
text: nextAssistantText,
|
|
1272
|
+
state: "streaming",
|
|
1273
|
+
createdAt: new Date().toISOString()
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
return nextAssistantText;
|
|
1277
|
+
}
|
|
1278
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1279
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1280
|
+
if (type === "response") {
|
|
1281
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1282
|
+
const success = input.record.success !== false;
|
|
1283
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1287
|
+
id: input.nextRunLogId(),
|
|
1288
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1289
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1290
|
+
tone: success ? "tool" : "error",
|
|
1291
|
+
status: input.status,
|
|
1292
|
+
payload: input.record,
|
|
1293
|
+
createdAt: new Date().toISOString()
|
|
1294
|
+
});
|
|
1295
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
if (type !== "extension_ui_request")
|
|
1299
|
+
return false;
|
|
1300
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1301
|
+
let title = "Pi UI event";
|
|
1302
|
+
let detail = method;
|
|
1303
|
+
let tone = "info";
|
|
1304
|
+
if (method === "notify") {
|
|
1305
|
+
title = "Pi notification";
|
|
1306
|
+
detail = String(input.record.message ?? "");
|
|
1307
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1308
|
+
} else if (method === "setStatus") {
|
|
1309
|
+
title = "Pi UI status";
|
|
1310
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1311
|
+
tone = "tool";
|
|
1312
|
+
} else if (method === "setWidget") {
|
|
1313
|
+
title = "Pi UI widget";
|
|
1314
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1315
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1316
|
+
tone = "tool";
|
|
1317
|
+
} else if (method === "setTitle") {
|
|
1318
|
+
title = "Pi UI title";
|
|
1319
|
+
detail = String(input.record.title ?? "");
|
|
1320
|
+
tone = "tool";
|
|
1321
|
+
} else if (method === "set_editor_text") {
|
|
1322
|
+
title = "Pi editor update";
|
|
1323
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1324
|
+
tone = "tool";
|
|
1325
|
+
} else {
|
|
1326
|
+
title = "Pi UI request";
|
|
1327
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1328
|
+
}
|
|
1329
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1330
|
+
id: input.nextRunLogId(),
|
|
1331
|
+
title,
|
|
1332
|
+
detail,
|
|
1333
|
+
tone,
|
|
1334
|
+
status: input.status,
|
|
1335
|
+
payload: input.record,
|
|
1336
|
+
createdAt: new Date().toISOString()
|
|
1337
|
+
});
|
|
1338
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1339
|
+
return true;
|
|
1340
|
+
}
|
|
1341
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1342
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1343
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1344
|
+
return false;
|
|
1345
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1346
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1347
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1348
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1349
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1350
|
+
type,
|
|
1351
|
+
toolName,
|
|
1352
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1353
|
+
createdAt: new Date().toISOString()
|
|
1354
|
+
});
|
|
1355
|
+
return true;
|
|
1356
|
+
}
|
|
1357
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1358
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1359
|
+
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");
|
|
1360
|
+
}
|
|
1361
|
+
function appendToolTimelineFromLog(input) {
|
|
1362
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1363
|
+
if (title !== "Tool activity")
|
|
1364
|
+
return;
|
|
1365
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1366
|
+
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;
|
|
1367
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1368
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1369
|
+
id: `tool:${logId}`,
|
|
1370
|
+
type: "tool_execution_update",
|
|
1371
|
+
toolName,
|
|
1372
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1373
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1374
|
+
payload,
|
|
1375
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
997
1378
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1379
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1380
|
return null;
|
|
1000
1381
|
}
|
|
1001
1382
|
try {
|
|
1002
|
-
const status =
|
|
1383
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1384
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1385
|
} catch {
|
|
1005
1386
|
return null;
|
|
@@ -1017,7 +1398,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1398
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1399
|
return sourceTask;
|
|
1019
1400
|
}
|
|
1020
|
-
|
|
1401
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1402
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1403
|
+
return null;
|
|
1404
|
+
const record = sourceTask;
|
|
1405
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1406
|
+
if (direct?.trim())
|
|
1407
|
+
return direct.trim();
|
|
1408
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1409
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1410
|
+
}
|
|
1411
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1412
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1413
|
+
id: input.taskId,
|
|
1414
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1415
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1416
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1417
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1418
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1419
|
+
});
|
|
1420
|
+
if (serverResult.ok === false) {
|
|
1421
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1422
|
+
}
|
|
1423
|
+
return {
|
|
1424
|
+
updated: serverResult.ok !== false,
|
|
1425
|
+
taskId: input.taskId,
|
|
1426
|
+
status: input.update.status,
|
|
1427
|
+
source: "server",
|
|
1428
|
+
sourceKind: "server",
|
|
1429
|
+
projectSync: serverResult.projectSync
|
|
1430
|
+
};
|
|
1431
|
+
};
|
|
1432
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1433
|
if (!taskId)
|
|
1022
1434
|
return;
|
|
1023
1435
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1107,11 +1519,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1107
1519
|
...input.model ? ["--model", input.model] : [],
|
|
1108
1520
|
"--prompt"
|
|
1109
1521
|
] : input.runtimeAdapter === "pi" ? [
|
|
1110
|
-
"
|
|
1111
|
-
"--verbose",
|
|
1112
|
-
"--mode",
|
|
1113
|
-
"json",
|
|
1114
|
-
"--no-session",
|
|
1522
|
+
"__rig_pi_session_daemon__",
|
|
1115
1523
|
...input.model ? ["--model", input.model] : []
|
|
1116
1524
|
] : [
|
|
1117
1525
|
"--print",
|
|
@@ -1186,15 +1594,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1186
1594
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1187
1595
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1188
1596
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1189
|
-
const planningArtifactPath =
|
|
1597
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1190
1598
|
const persistedPlanning = {
|
|
1191
1599
|
...planningClassification,
|
|
1192
1600
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1193
1601
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1194
1602
|
classifiedAt: new Date().toISOString()
|
|
1195
1603
|
};
|
|
1196
|
-
|
|
1197
|
-
|
|
1604
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1605
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1198
1606
|
`, "utf8");
|
|
1199
1607
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1200
1608
|
prompt = `${prompt}
|
|
@@ -1208,7 +1616,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1208
1616
|
projectRoot: context.projectRoot,
|
|
1209
1617
|
runId: input.runId,
|
|
1210
1618
|
stage,
|
|
1211
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1619
|
+
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
1620
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1213
1621
|
});
|
|
1214
1622
|
}
|
|
@@ -1244,11 +1652,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1244
1652
|
let verificationStarted = false;
|
|
1245
1653
|
let reviewStarted = false;
|
|
1246
1654
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1247
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1655
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1248
1656
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1249
1657
|
let latestProviderCommand = null;
|
|
1250
1658
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1251
1659
|
let snapshotSidecarPromise = null;
|
|
1660
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1252
1661
|
let dirtyBaselineApplied = false;
|
|
1253
1662
|
const childEnv = {
|
|
1254
1663
|
...process.env,
|
|
@@ -1301,6 +1710,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1301
1710
|
detail: detail ?? "Verifier review is running."
|
|
1302
1711
|
});
|
|
1303
1712
|
};
|
|
1713
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1304
1714
|
const handleWrapperEvent = (rawPayload) => {
|
|
1305
1715
|
let event = null;
|
|
1306
1716
|
try {
|
|
@@ -1316,6 +1726,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1316
1726
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1317
1727
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1318
1728
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1729
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1319
1730
|
latestRuntimeBranch = runtimeId;
|
|
1320
1731
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1321
1732
|
runtimeId: runtimeId ?? null,
|
|
@@ -1327,10 +1738,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1327
1738
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1328
1739
|
status: "running",
|
|
1329
1740
|
worktreePath: latestRuntimeWorkspace,
|
|
1330
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1741
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1331
1742
|
logRoot: latestLogsDir,
|
|
1332
|
-
sessionPath: latestSessionDir ?
|
|
1333
|
-
sessionLogPath: latestLogsDir ?
|
|
1743
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1744
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1334
1745
|
branch: runtimeId
|
|
1335
1746
|
});
|
|
1336
1747
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1338,8 +1749,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1338
1749
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1339
1750
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1340
1751
|
if (readyFile) {
|
|
1341
|
-
|
|
1342
|
-
|
|
1752
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1753
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1343
1754
|
`, "utf8");
|
|
1344
1755
|
}
|
|
1345
1756
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1353,7 +1764,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1353
1764
|
});
|
|
1354
1765
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1355
1766
|
}
|
|
1356
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1767
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1357
1768
|
snapshotSidecarPromise = (async () => {
|
|
1358
1769
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1359
1770
|
projectRoot: context.projectRoot,
|
|
@@ -1435,9 +1846,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1435
1846
|
}
|
|
1436
1847
|
return true;
|
|
1437
1848
|
}
|
|
1849
|
+
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") {
|
|
1850
|
+
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";
|
|
1851
|
+
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");
|
|
1852
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1853
|
+
id: nextRunLogId(),
|
|
1854
|
+
title,
|
|
1855
|
+
detail,
|
|
1856
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1857
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1858
|
+
payload: {
|
|
1859
|
+
eventType: event.type,
|
|
1860
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1861
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1862
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1863
|
+
},
|
|
1864
|
+
createdAt: new Date().toISOString()
|
|
1865
|
+
});
|
|
1866
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
if (event.type === "pi.session.ready") {
|
|
1870
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1871
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1872
|
+
if (privateMetadata) {
|
|
1873
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1874
|
+
piSession: publicMetadata,
|
|
1875
|
+
piSessionPrivate: privateMetadata
|
|
1876
|
+
});
|
|
1877
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1878
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1879
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1880
|
+
title: "Worker Pi session ready",
|
|
1881
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1882
|
+
tone: "info",
|
|
1883
|
+
status: "running",
|
|
1884
|
+
payload: {
|
|
1885
|
+
sessionId: sessionId || null,
|
|
1886
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1887
|
+
},
|
|
1888
|
+
createdAt: new Date().toISOString()
|
|
1889
|
+
});
|
|
1890
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1891
|
+
}
|
|
1892
|
+
return true;
|
|
1893
|
+
}
|
|
1894
|
+
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") {
|
|
1895
|
+
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";
|
|
1896
|
+
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")}`;
|
|
1897
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1898
|
+
id: nextRunLogId(),
|
|
1899
|
+
title,
|
|
1900
|
+
detail,
|
|
1901
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1902
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1903
|
+
payload,
|
|
1904
|
+
createdAt: new Date().toISOString()
|
|
1905
|
+
});
|
|
1906
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1907
|
+
return true;
|
|
1908
|
+
}
|
|
1438
1909
|
return false;
|
|
1439
1910
|
};
|
|
1440
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1441
1911
|
const handleAgentStdoutLine = (line) => {
|
|
1442
1912
|
const trimmed = line.trim();
|
|
1443
1913
|
if (!trimmed)
|
|
@@ -1471,6 +1941,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1471
1941
|
try {
|
|
1472
1942
|
const record = JSON.parse(trimmed);
|
|
1473
1943
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1944
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1945
|
+
projectRoot: context.projectRoot,
|
|
1946
|
+
runId: input.runId,
|
|
1947
|
+
record,
|
|
1948
|
+
status: liveLogStatus,
|
|
1949
|
+
nextRunLogId
|
|
1950
|
+
})) {
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1954
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1474
1957
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1475
1958
|
runId: input.runId,
|
|
1476
1959
|
record,
|
|
@@ -1487,7 +1970,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1487
1970
|
if (providerLogs.length > 0) {
|
|
1488
1971
|
for (const providerLog of providerLogs) {
|
|
1489
1972
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1973
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1490
1974
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1975
|
+
if (providerLog.title === "Tool activity")
|
|
1976
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1491
1977
|
}
|
|
1492
1978
|
}
|
|
1493
1979
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1539,6 +2025,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1539
2025
|
return;
|
|
1540
2026
|
}
|
|
1541
2027
|
}
|
|
2028
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
2029
|
+
return;
|
|
2030
|
+
}
|
|
1542
2031
|
if (record.type === "assistant") {
|
|
1543
2032
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1544
2033
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1645,7 +2134,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1645
2134
|
let acceptedArtifactObservedAt = null;
|
|
1646
2135
|
let acceptedArtifactPollTimer = null;
|
|
1647
2136
|
let acceptedArtifactKillTimer = null;
|
|
1648
|
-
const attemptExit = await new Promise((
|
|
2137
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1649
2138
|
let settled = false;
|
|
1650
2139
|
const settle = (result) => {
|
|
1651
2140
|
if (settled)
|
|
@@ -1653,7 +2142,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1653
2142
|
settled = true;
|
|
1654
2143
|
if (acceptedArtifactPollTimer)
|
|
1655
2144
|
clearInterval(acceptedArtifactPollTimer);
|
|
1656
|
-
|
|
2145
|
+
resolve7(result);
|
|
1657
2146
|
};
|
|
1658
2147
|
const pollAcceptedArtifacts = () => {
|
|
1659
2148
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1726,7 +2215,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1726
2215
|
});
|
|
1727
2216
|
for (const pendingLog of pendingLogs) {
|
|
1728
2217
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2218
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1729
2219
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2220
|
+
if (pendingLog.title === "Tool activity")
|
|
2221
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1730
2222
|
}
|
|
1731
2223
|
process.off("SIGTERM", forwardSigterm);
|
|
1732
2224
|
if (attemptExit.error) {
|
|
@@ -1852,8 +2344,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1852
2344
|
}
|
|
1853
2345
|
if (planningClassification.planningRequired) {
|
|
1854
2346
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1855
|
-
const expectedPlanPath =
|
|
1856
|
-
if (!
|
|
2347
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2348
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1857
2349
|
const failedAt = new Date().toISOString();
|
|
1858
2350
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1859
2351
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1873,6 +2365,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1873
2365
|
throw new CliError2(failureDetail, 1);
|
|
1874
2366
|
}
|
|
1875
2367
|
}
|
|
2368
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2369
|
+
appendPiStageLog({
|
|
2370
|
+
projectRoot: context.projectRoot,
|
|
2371
|
+
runId: input.runId,
|
|
2372
|
+
stage: "Validate",
|
|
2373
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2374
|
+
status: "completed"
|
|
2375
|
+
});
|
|
2376
|
+
if (verificationAction && !reviewStarted) {
|
|
2377
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2378
|
+
}
|
|
2379
|
+
if (!reviewAction) {
|
|
2380
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2381
|
+
}
|
|
2382
|
+
if (reviewAction) {
|
|
2383
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2384
|
+
}
|
|
2385
|
+
const requestedAt = new Date().toISOString();
|
|
2386
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2387
|
+
status: "reviewing",
|
|
2388
|
+
completedAt: null,
|
|
2389
|
+
errorText: null,
|
|
2390
|
+
serverCloseout: {
|
|
2391
|
+
status: "pending",
|
|
2392
|
+
phase: "queued",
|
|
2393
|
+
requestedAt,
|
|
2394
|
+
updatedAt: requestedAt,
|
|
2395
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2396
|
+
branch: latestRuntimeBranch,
|
|
2397
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2401
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2402
|
+
title: "Server-owned closeout requested",
|
|
2403
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2404
|
+
tone: "info",
|
|
2405
|
+
status: "reviewing",
|
|
2406
|
+
createdAt: requestedAt,
|
|
2407
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2408
|
+
});
|
|
2409
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2410
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2411
|
+
await context.emitEvent("command.finished", {
|
|
2412
|
+
command: [
|
|
2413
|
+
"rig",
|
|
2414
|
+
"server",
|
|
2415
|
+
"task-run",
|
|
2416
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2417
|
+
...input.title ? ["--title", input.title] : []
|
|
2418
|
+
],
|
|
2419
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2420
|
+
exitCode: 0,
|
|
2421
|
+
durationMs: 0,
|
|
2422
|
+
startedAt,
|
|
2423
|
+
finishedAt: requestedAt
|
|
2424
|
+
});
|
|
2425
|
+
process.exit(0);
|
|
2426
|
+
}
|
|
1876
2427
|
const runPiPrFeedbackFix = async (message) => {
|
|
1877
2428
|
appendPiStageLog({
|
|
1878
2429
|
projectRoot: context.projectRoot,
|
|
@@ -1900,11 +2451,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1900
2451
|
child.stdin.write(message);
|
|
1901
2452
|
}
|
|
1902
2453
|
child.stdin.end();
|
|
2454
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2455
|
+
let feedbackAssistantText = "";
|
|
2456
|
+
const feedbackPendingToolUses = new Map;
|
|
1903
2457
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1904
2458
|
stdout.on("line", (line) => {
|
|
1905
2459
|
const trimmed = line.trim();
|
|
1906
2460
|
if (!trimmed)
|
|
1907
2461
|
return;
|
|
2462
|
+
try {
|
|
2463
|
+
const record = JSON.parse(trimmed);
|
|
2464
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2465
|
+
runId: input.runId,
|
|
2466
|
+
record,
|
|
2467
|
+
createdAtFallback: new Date().toISOString(),
|
|
2468
|
+
status: "reviewing",
|
|
2469
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2470
|
+
});
|
|
2471
|
+
for (const providerLog of providerLogs) {
|
|
2472
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2473
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2474
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2475
|
+
if (providerLog.title === "Tool activity")
|
|
2476
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2477
|
+
}
|
|
2478
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2479
|
+
projectRoot: context.projectRoot,
|
|
2480
|
+
runId: input.runId,
|
|
2481
|
+
messageId: feedbackAssistantMessageId,
|
|
2482
|
+
record,
|
|
2483
|
+
assistantText: feedbackAssistantText
|
|
2484
|
+
});
|
|
2485
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2486
|
+
if (hadAssistantDelta) {
|
|
2487
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2488
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2489
|
+
}
|
|
2490
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2491
|
+
return;
|
|
2492
|
+
} catch {}
|
|
1908
2493
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1909
2494
|
id: nextRunLogId(),
|
|
1910
2495
|
title: "Pi PR feedback fix output",
|
|
@@ -1930,10 +2515,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1930
2515
|
});
|
|
1931
2516
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1932
2517
|
});
|
|
1933
|
-
const exitCode = await new Promise((
|
|
1934
|
-
child.once("error", () =>
|
|
1935
|
-
child.once("close", (code) =>
|
|
2518
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2519
|
+
child.once("error", () => resolve7(1));
|
|
2520
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1936
2521
|
});
|
|
2522
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2523
|
+
runId: input.runId,
|
|
2524
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2525
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2526
|
+
})) {
|
|
2527
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2528
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2529
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2530
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2531
|
+
}
|
|
2532
|
+
if (feedbackAssistantText.trim()) {
|
|
2533
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2534
|
+
id: feedbackAssistantMessageId,
|
|
2535
|
+
type: "assistant_message",
|
|
2536
|
+
text: feedbackAssistantText,
|
|
2537
|
+
state: "completed",
|
|
2538
|
+
createdAt: new Date().toISOString(),
|
|
2539
|
+
completedAt: new Date().toISOString()
|
|
2540
|
+
});
|
|
2541
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2542
|
+
}
|
|
1937
2543
|
if (exitCode !== 0) {
|
|
1938
2544
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1939
2545
|
}
|