@h-rig/cli 0.0.6-alpha.4 → 0.0.6-alpha.40
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 +3947 -1190
- package/dist/src/commands/_cli-format.js +369 -0
- package/dist/src/commands/_connection-state.js +1 -3
- package/dist/src/commands/_doctor-checks.js +13 -27
- package/dist/src/commands/_help-catalog.js +445 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.js +942 -56
- package/dist/src/commands/_parsers.js +0 -2
- package/dist/src/commands/_pi-frontend.js +906 -0
- package/dist/src/commands/_pi-install.js +4 -3
- package/dist/src/commands/_pi-worker-bridge-extension.js +826 -0
- package/dist/src/commands/_policy.js +0 -2
- package/dist/src/commands/_preflight.js +32 -109
- package/dist/src/commands/_run-driver-helpers.js +21 -2
- package/dist/src/commands/_server-client.js +152 -31
- package/dist/src/commands/_snapshot-upload.js +8 -23
- package/dist/src/commands/_task-picker.js +44 -16
- package/dist/src/commands/agent.js +8 -9
- package/dist/src/commands/browser.js +4 -6
- package/dist/src/commands/connect.js +132 -25
- package/dist/src/commands/dist.js +4 -6
- package/dist/src/commands/doctor.js +13 -27
- package/dist/src/commands/github.js +10 -25
- package/dist/src/commands/inbox.js +351 -31
- package/dist/src/commands/init.js +298 -71
- package/dist/src/commands/inspect.js +237 -23
- package/dist/src/commands/inspector.js +2 -4
- package/dist/src/commands/pi.js +168 -0
- package/dist/src/commands/plugin.js +81 -22
- package/dist/src/commands/profile-and-review.js +8 -10
- package/dist/src/commands/queue.js +2 -3
- package/dist/src/commands/remote.js +18 -20
- package/dist/src/commands/repo-git-harness.js +6 -8
- package/dist/src/commands/run.js +1240 -123
- package/dist/src/commands/server.js +222 -33
- package/dist/src/commands/setup.js +17 -37
- package/dist/src/commands/task-report-bug.js +5 -7
- package/dist/src/commands/task-run-driver.js +649 -71
- package/dist/src/commands/task.js +1679 -252
- package/dist/src/commands/test.js +3 -5
- package/dist/src/commands/workspace.js +4 -6
- package/dist/src/commands.js +3927 -1164
- package/dist/src/index.js +3940 -1186
- package/dist/src/launcher.js +5 -3
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.js +5 -19
- package/package.json +6 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/task-run-driver.ts
|
|
3
|
-
import { copyFileSync, existsSync as
|
|
4
|
-
import { resolve as
|
|
3
|
+
import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4
|
+
import { resolve as resolve6 } from "path";
|
|
5
5
|
import { spawn, spawnSync } from "child_process";
|
|
6
6
|
import { createInterface as createLineInterface } from "readline";
|
|
7
7
|
|
|
@@ -9,8 +9,6 @@ import { createInterface as createLineInterface } from "readline";
|
|
|
9
9
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
10
10
|
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
11
11
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
12
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
13
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
14
12
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
15
13
|
import { CliError as CliError2 } from "@rig/runtime/control-plane/errors";
|
|
16
14
|
function formatCommand(parts) {
|
|
@@ -39,14 +37,14 @@ import {
|
|
|
39
37
|
isCodexExecRecord
|
|
40
38
|
} from "@rig/runtime/control-plane/provider/codex-exec-records";
|
|
41
39
|
import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
|
|
42
|
-
import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
40
|
+
import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
43
41
|
import {
|
|
44
|
-
buildTaskRunLifecycleComment
|
|
45
|
-
updateConfiguredTaskSourceTask
|
|
42
|
+
buildTaskRunLifecycleComment
|
|
46
43
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
47
44
|
import {
|
|
48
45
|
closeIssueAfterMergedPr,
|
|
49
46
|
commitRunChanges,
|
|
47
|
+
pushBranchSyncedWithOrigin,
|
|
50
48
|
runPrAutomation
|
|
51
49
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
52
50
|
|
|
@@ -317,6 +315,7 @@ ${acceptance}` : null,
|
|
|
317
315
|
${sourceContractLines.join(`
|
|
318
316
|
`)}` : null,
|
|
319
317
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
318
|
+
readPriorPrProgressForPrompt(input.projectRoot, input.taskId),
|
|
320
319
|
initialPrompt ? `Additional operator guidance:
|
|
321
320
|
${initialPrompt}` : null,
|
|
322
321
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -326,6 +325,26 @@ ${providerLines.join(" ")}` : null,
|
|
|
326
325
|
|
|
327
326
|
`);
|
|
328
327
|
}
|
|
328
|
+
function readPriorPrProgressForPrompt(projectRoot, taskId) {
|
|
329
|
+
for (const candidate of [
|
|
330
|
+
resolve3(projectRoot, ".worktrees", taskId, "artifacts", taskId, "pr-state.json"),
|
|
331
|
+
resolve3(projectRoot, "artifacts", taskId, "pr-state.json")
|
|
332
|
+
]) {
|
|
333
|
+
try {
|
|
334
|
+
const raw = JSON.parse(readFileSync(candidate, "utf8"));
|
|
335
|
+
const entries = Array.isArray(raw) ? raw : [raw];
|
|
336
|
+
const first = entries.find((entry) => entry && typeof entry === "object" && typeof entry.url === "string");
|
|
337
|
+
if (!first)
|
|
338
|
+
continue;
|
|
339
|
+
return [
|
|
340
|
+
`Prior progress exists for this task: PR ${first.url}${first.branch ? ` (branch ${first.branch})` : ""} was opened by an earlier run.`,
|
|
341
|
+
"Check its current state first (diff, checks, review). If the work is already complete and checks are green,",
|
|
342
|
+
"run `rig-agent completion-verification` to merge and close instead of re-implementing anything."
|
|
343
|
+
].join(" ");
|
|
344
|
+
} catch {}
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
329
348
|
function firstPromptString(...values) {
|
|
330
349
|
for (const value of values) {
|
|
331
350
|
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
@@ -388,6 +407,194 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
388
407
|
`);
|
|
389
408
|
}
|
|
390
409
|
|
|
410
|
+
// packages/cli/src/commands/_server-client.ts
|
|
411
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
412
|
+
import { resolve as resolve5 } from "path";
|
|
413
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
414
|
+
|
|
415
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
416
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
417
|
+
import { homedir } from "os";
|
|
418
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
419
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
420
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
421
|
+
if (explicit)
|
|
422
|
+
return resolve4(explicit);
|
|
423
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
424
|
+
if (stateDir)
|
|
425
|
+
return resolve4(stateDir, "connections.json");
|
|
426
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
427
|
+
}
|
|
428
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
429
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
430
|
+
}
|
|
431
|
+
function readJsonFile(path) {
|
|
432
|
+
if (!existsSync2(path))
|
|
433
|
+
return null;
|
|
434
|
+
try {
|
|
435
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
436
|
+
} catch (error) {
|
|
437
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function normalizeConnection(value) {
|
|
441
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
442
|
+
return null;
|
|
443
|
+
const record = value;
|
|
444
|
+
if (record.kind === "local")
|
|
445
|
+
return { kind: "local", mode: "auto" };
|
|
446
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
447
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
448
|
+
return { kind: "remote", baseUrl };
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
function readGlobalConnections(options = {}) {
|
|
453
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
454
|
+
const payload = readJsonFile(path);
|
|
455
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
456
|
+
return { connections: {} };
|
|
457
|
+
}
|
|
458
|
+
const rawConnections = payload.connections;
|
|
459
|
+
const connections = {};
|
|
460
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
461
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
462
|
+
const connection = normalizeConnection(raw);
|
|
463
|
+
if (connection)
|
|
464
|
+
connections[alias] = connection;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return { connections };
|
|
468
|
+
}
|
|
469
|
+
function readRepoConnection(projectRoot) {
|
|
470
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
471
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
472
|
+
return null;
|
|
473
|
+
const record = payload;
|
|
474
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
475
|
+
if (!selected)
|
|
476
|
+
return null;
|
|
477
|
+
return {
|
|
478
|
+
selected,
|
|
479
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
480
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
484
|
+
const repo = readRepoConnection(projectRoot);
|
|
485
|
+
if (!repo)
|
|
486
|
+
return null;
|
|
487
|
+
if (repo.selected === "local")
|
|
488
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
489
|
+
const global = readGlobalConnections(options);
|
|
490
|
+
const connection = global.connections[repo.selected];
|
|
491
|
+
if (!connection) {
|
|
492
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
493
|
+
}
|
|
494
|
+
return { alias: repo.selected, connection };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// packages/cli/src/commands/_server-client.ts
|
|
498
|
+
var scopedGitHubBearerTokens = new Map;
|
|
499
|
+
function cleanToken(value) {
|
|
500
|
+
const trimmed = value?.trim();
|
|
501
|
+
return trimmed ? trimmed : null;
|
|
502
|
+
}
|
|
503
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
504
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
505
|
+
if (!existsSync3(path))
|
|
506
|
+
return null;
|
|
507
|
+
try {
|
|
508
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
509
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
510
|
+
} catch {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
515
|
+
const scopedKey = resolve5(projectRoot);
|
|
516
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
517
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
518
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
519
|
+
if (privateSession)
|
|
520
|
+
return privateSession;
|
|
521
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
522
|
+
}
|
|
523
|
+
async function ensureServerForCli(projectRoot) {
|
|
524
|
+
try {
|
|
525
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
526
|
+
if (selected?.connection.kind === "remote") {
|
|
527
|
+
return {
|
|
528
|
+
baseUrl: selected.connection.baseUrl,
|
|
529
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
530
|
+
connectionKind: "remote"
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
534
|
+
return {
|
|
535
|
+
baseUrl: connection.baseUrl,
|
|
536
|
+
authToken: connection.authToken,
|
|
537
|
+
connectionKind: "local"
|
|
538
|
+
};
|
|
539
|
+
} catch (error) {
|
|
540
|
+
if (error instanceof Error) {
|
|
541
|
+
throw new CliError2(error.message, 1);
|
|
542
|
+
}
|
|
543
|
+
throw error;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function mergeHeaders(headers, authToken) {
|
|
547
|
+
const merged = new Headers(headers);
|
|
548
|
+
if (authToken) {
|
|
549
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
550
|
+
}
|
|
551
|
+
return merged;
|
|
552
|
+
}
|
|
553
|
+
function diagnosticMessage(payload) {
|
|
554
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
555
|
+
return null;
|
|
556
|
+
const record = payload;
|
|
557
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
558
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
559
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
560
|
+
return [];
|
|
561
|
+
const diagnostic = entry;
|
|
562
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
563
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
564
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
565
|
+
});
|
|
566
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
567
|
+
}
|
|
568
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
569
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
570
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
571
|
+
...init,
|
|
572
|
+
headers: mergeHeaders(init.headers, server.authToken)
|
|
573
|
+
});
|
|
574
|
+
const text = await response.text();
|
|
575
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
576
|
+
try {
|
|
577
|
+
return JSON.parse(text);
|
|
578
|
+
} catch {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
})() : null;
|
|
582
|
+
if (!response.ok) {
|
|
583
|
+
const diagnostics = diagnosticMessage(payload);
|
|
584
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
585
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
586
|
+
}
|
|
587
|
+
return payload;
|
|
588
|
+
}
|
|
589
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
590
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: { "content-type": "application/json" },
|
|
593
|
+
body: JSON.stringify(input)
|
|
594
|
+
});
|
|
595
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
596
|
+
}
|
|
597
|
+
|
|
391
598
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
599
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
600
|
"Connect",
|
|
@@ -431,6 +638,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
638
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
639
|
RIG_TASK_ID: input.taskId,
|
|
433
640
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
641
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
642
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
643
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
644
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +663,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
663
|
return 0;
|
|
456
664
|
let copied = 0;
|
|
457
665
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
666
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
667
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
668
|
try {
|
|
461
669
|
if (!statSync(sourcePath).isFile())
|
|
462
670
|
continue;
|
|
463
|
-
|
|
671
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
672
|
copyFileSync(sourcePath, targetPath);
|
|
465
673
|
copied += 1;
|
|
466
674
|
} catch {}
|
|
@@ -499,7 +707,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
707
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
708
|
return {
|
|
501
709
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
710
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
711
|
};
|
|
504
712
|
}
|
|
505
713
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +715,7 @@ function positiveInt(value, fallback) {
|
|
|
507
715
|
}
|
|
508
716
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
717
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
718
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
719
|
return {
|
|
512
720
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
721
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +809,6 @@ function appendPiStageLog(input) {
|
|
|
601
809
|
});
|
|
602
810
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
811
|
}
|
|
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
812
|
function createCommandRunner(binary) {
|
|
611
813
|
return async (args, options) => {
|
|
612
814
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +819,9 @@ function createCommandRunner(binary) {
|
|
|
617
819
|
const stderrChunks = [];
|
|
618
820
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
821
|
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) =>
|
|
822
|
+
return await new Promise((resolve7) => {
|
|
823
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
824
|
+
child.once("close", (code) => resolve7({
|
|
623
825
|
exitCode: code ?? 1,
|
|
624
826
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
827
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +845,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
845
|
if (!taskId) {
|
|
644
846
|
return { status: "skipped" };
|
|
645
847
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
848
|
+
const configInput = input.config ?? null;
|
|
849
|
+
const config = configInput ?? {};
|
|
850
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
851
|
if (prMode === "off" || prMode === "ask") {
|
|
649
852
|
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
853
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +863,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
863
|
gitCommand
|
|
661
864
|
});
|
|
662
865
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
866
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
867
|
const stage = input.appendStage ?? (() => {
|
|
665
868
|
return;
|
|
666
869
|
});
|
|
@@ -682,7 +885,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
885
|
command: gitCommand
|
|
683
886
|
});
|
|
684
887
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
888
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
889
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
890
|
const pr = await prAutomation({
|
|
688
891
|
projectRoot: workspace,
|
|
@@ -692,7 +895,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
895
|
config,
|
|
693
896
|
sourceTask: input.sourceTask,
|
|
694
897
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
898
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
899
|
command: ghCommand,
|
|
900
|
+
gitCommand,
|
|
696
901
|
steerPi,
|
|
697
902
|
lifecycle: {
|
|
698
903
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +946,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
946
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
947
|
})
|
|
743
948
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
949
|
});
|
|
747
950
|
}
|
|
748
951
|
},
|
|
@@ -761,8 +964,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
964
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
965
|
})
|
|
763
966
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
967
|
});
|
|
767
968
|
}
|
|
768
969
|
},
|
|
@@ -791,8 +992,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
992
|
errorText: detail
|
|
792
993
|
})
|
|
793
994
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
995
|
+
}).catch((error) => {
|
|
996
|
+
try {
|
|
997
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
998
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
999
|
+
title: "Task source needs-attention update failed",
|
|
1000
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1001
|
+
tone: "error",
|
|
1002
|
+
status: "needs_attention",
|
|
1003
|
+
createdAt: new Date().toISOString()
|
|
1004
|
+
});
|
|
1005
|
+
} catch {}
|
|
796
1006
|
});
|
|
797
1007
|
}
|
|
798
1008
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1025,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1025
|
return null;
|
|
816
1026
|
}
|
|
817
1027
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1028
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1029
|
if (!summary || summary.status !== "fail") {
|
|
820
1030
|
continue;
|
|
821
1031
|
}
|
|
@@ -896,14 +1106,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1106
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1107
|
return { accepted: false, reason: null };
|
|
898
1108
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1109
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1110
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1111
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1112
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1113
|
if (reviewStatus !== "APPROVED") {
|
|
904
1114
|
return { accepted: false, reason: null };
|
|
905
1115
|
}
|
|
906
|
-
const taskResult =
|
|
1116
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1117
|
if (taskResult?.status !== "completed") {
|
|
908
1118
|
return { accepted: false, reason: null };
|
|
909
1119
|
}
|
|
@@ -935,13 +1145,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1145
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1146
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1147
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1148
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1149
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1150
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1151
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1152
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1153
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1154
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1155
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1156
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1157
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1204,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1204
|
}
|
|
995
1205
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1206
|
}
|
|
1207
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1208
|
+
let nextAssistantText = input.assistantText;
|
|
1209
|
+
if (input.record.type === "message_update") {
|
|
1210
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1211
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1212
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1213
|
+
}
|
|
1214
|
+
} else if (input.record.type === "stream_event") {
|
|
1215
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1216
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1217
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1218
|
+
nextAssistantText += delta.text;
|
|
1219
|
+
}
|
|
1220
|
+
} else if (input.record.type === "assistant") {
|
|
1221
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1222
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1223
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1224
|
+
if (fullText.length > nextAssistantText.length)
|
|
1225
|
+
nextAssistantText = fullText;
|
|
1226
|
+
}
|
|
1227
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1228
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1229
|
+
id: input.messageId,
|
|
1230
|
+
type: "assistant_message",
|
|
1231
|
+
text: nextAssistantText,
|
|
1232
|
+
state: "streaming",
|
|
1233
|
+
createdAt: new Date().toISOString()
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
return nextAssistantText;
|
|
1237
|
+
}
|
|
1238
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1239
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1240
|
+
if (type === "response") {
|
|
1241
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1242
|
+
const success = input.record.success !== false;
|
|
1243
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1244
|
+
return true;
|
|
1245
|
+
}
|
|
1246
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1247
|
+
id: input.nextRunLogId(),
|
|
1248
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1249
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1250
|
+
tone: success ? "tool" : "error",
|
|
1251
|
+
status: input.status,
|
|
1252
|
+
payload: input.record,
|
|
1253
|
+
createdAt: new Date().toISOString()
|
|
1254
|
+
});
|
|
1255
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1256
|
+
return true;
|
|
1257
|
+
}
|
|
1258
|
+
if (type !== "extension_ui_request")
|
|
1259
|
+
return false;
|
|
1260
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1261
|
+
let title = "Pi UI event";
|
|
1262
|
+
let detail = method;
|
|
1263
|
+
let tone = "info";
|
|
1264
|
+
if (method === "notify") {
|
|
1265
|
+
title = "Pi notification";
|
|
1266
|
+
detail = String(input.record.message ?? "");
|
|
1267
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1268
|
+
} else if (method === "setStatus") {
|
|
1269
|
+
title = "Pi UI status";
|
|
1270
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1271
|
+
tone = "tool";
|
|
1272
|
+
} else if (method === "setWidget") {
|
|
1273
|
+
title = "Pi UI widget";
|
|
1274
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1275
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1276
|
+
tone = "tool";
|
|
1277
|
+
} else if (method === "setTitle") {
|
|
1278
|
+
title = "Pi UI title";
|
|
1279
|
+
detail = String(input.record.title ?? "");
|
|
1280
|
+
tone = "tool";
|
|
1281
|
+
} else if (method === "set_editor_text") {
|
|
1282
|
+
title = "Pi editor update";
|
|
1283
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1284
|
+
tone = "tool";
|
|
1285
|
+
} else {
|
|
1286
|
+
title = "Pi UI request";
|
|
1287
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1288
|
+
}
|
|
1289
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1290
|
+
id: input.nextRunLogId(),
|
|
1291
|
+
title,
|
|
1292
|
+
detail,
|
|
1293
|
+
tone,
|
|
1294
|
+
status: input.status,
|
|
1295
|
+
payload: input.record,
|
|
1296
|
+
createdAt: new Date().toISOString()
|
|
1297
|
+
});
|
|
1298
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1302
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1303
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1304
|
+
return false;
|
|
1305
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1306
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1307
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1308
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1309
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1310
|
+
type,
|
|
1311
|
+
toolName,
|
|
1312
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1313
|
+
createdAt: new Date().toISOString()
|
|
1314
|
+
});
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1318
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1319
|
+
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");
|
|
1320
|
+
}
|
|
1321
|
+
function appendToolTimelineFromLog(input) {
|
|
1322
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1323
|
+
if (title !== "Tool activity")
|
|
1324
|
+
return;
|
|
1325
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1326
|
+
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;
|
|
1327
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1328
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1329
|
+
id: `tool:${logId}`,
|
|
1330
|
+
type: "tool_execution_update",
|
|
1331
|
+
toolName,
|
|
1332
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1333
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1334
|
+
payload,
|
|
1335
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
997
1338
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1339
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1340
|
return null;
|
|
1000
1341
|
}
|
|
1001
1342
|
try {
|
|
1002
|
-
const status =
|
|
1343
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1344
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1345
|
} catch {
|
|
1005
1346
|
return null;
|
|
@@ -1017,7 +1358,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1358
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1359
|
return sourceTask;
|
|
1019
1360
|
}
|
|
1020
|
-
|
|
1361
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1362
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1363
|
+
return null;
|
|
1364
|
+
const record = sourceTask;
|
|
1365
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1366
|
+
if (direct?.trim())
|
|
1367
|
+
return direct.trim();
|
|
1368
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1369
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1370
|
+
}
|
|
1371
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1372
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1373
|
+
id: input.taskId,
|
|
1374
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1375
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1376
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1377
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1378
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1379
|
+
});
|
|
1380
|
+
if (serverResult.ok === false) {
|
|
1381
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1382
|
+
}
|
|
1383
|
+
return {
|
|
1384
|
+
updated: serverResult.ok !== false,
|
|
1385
|
+
taskId: input.taskId,
|
|
1386
|
+
status: input.update.status,
|
|
1387
|
+
source: "server",
|
|
1388
|
+
sourceKind: "server",
|
|
1389
|
+
projectSync: serverResult.projectSync
|
|
1390
|
+
};
|
|
1391
|
+
};
|
|
1392
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1393
|
if (!taskId)
|
|
1022
1394
|
return;
|
|
1023
1395
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1084,6 +1456,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1084
1456
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
1085
1457
|
const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
|
|
1086
1458
|
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
1459
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
1087
1460
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
1088
1461
|
let prompt = buildRunPrompt({
|
|
1089
1462
|
projectRoot: context.projectRoot,
|
|
@@ -1106,11 +1479,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1106
1479
|
...input.model ? ["--model", input.model] : [],
|
|
1107
1480
|
"--prompt"
|
|
1108
1481
|
] : input.runtimeAdapter === "pi" ? [
|
|
1109
|
-
"
|
|
1110
|
-
"--verbose",
|
|
1111
|
-
"--mode",
|
|
1112
|
-
"json",
|
|
1113
|
-
"--no-session",
|
|
1482
|
+
"__rig_pi_session_daemon__",
|
|
1114
1483
|
...input.model ? ["--model", input.model] : []
|
|
1115
1484
|
] : [
|
|
1116
1485
|
"--print",
|
|
@@ -1185,15 +1554,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1185
1554
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1186
1555
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1187
1556
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1188
|
-
const planningArtifactPath =
|
|
1557
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1189
1558
|
const persistedPlanning = {
|
|
1190
1559
|
...planningClassification,
|
|
1191
1560
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1192
1561
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1193
1562
|
classifiedAt: new Date().toISOString()
|
|
1194
1563
|
};
|
|
1195
|
-
|
|
1196
|
-
|
|
1564
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1565
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1197
1566
|
`, "utf8");
|
|
1198
1567
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1199
1568
|
prompt = `${prompt}
|
|
@@ -1207,7 +1576,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1207
1576
|
projectRoot: context.projectRoot,
|
|
1208
1577
|
runId: input.runId,
|
|
1209
1578
|
stage,
|
|
1210
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1579
|
+
detail: stage === "Launch Pi" ? "Worker Pi SDK session daemon starting; local frontend will attach over Rig-proxied WebSocket." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running in the worker runtime." : null,
|
|
1211
1580
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1212
1581
|
});
|
|
1213
1582
|
}
|
|
@@ -1243,11 +1612,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1243
1612
|
let verificationStarted = false;
|
|
1244
1613
|
let reviewStarted = false;
|
|
1245
1614
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1246
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1615
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1247
1616
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1248
1617
|
let latestProviderCommand = null;
|
|
1249
1618
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1250
1619
|
let snapshotSidecarPromise = null;
|
|
1620
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1251
1621
|
let dirtyBaselineApplied = false;
|
|
1252
1622
|
const childEnv = {
|
|
1253
1623
|
...process.env,
|
|
@@ -1300,6 +1670,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1300
1670
|
detail: detail ?? "Verifier review is running."
|
|
1301
1671
|
});
|
|
1302
1672
|
};
|
|
1673
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1303
1674
|
const handleWrapperEvent = (rawPayload) => {
|
|
1304
1675
|
let event = null;
|
|
1305
1676
|
try {
|
|
@@ -1315,6 +1686,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1315
1686
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1316
1687
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1317
1688
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1689
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1318
1690
|
latestRuntimeBranch = runtimeId;
|
|
1319
1691
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1320
1692
|
runtimeId: runtimeId ?? null,
|
|
@@ -1326,10 +1698,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1326
1698
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1327
1699
|
status: "running",
|
|
1328
1700
|
worktreePath: latestRuntimeWorkspace,
|
|
1329
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1701
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1330
1702
|
logRoot: latestLogsDir,
|
|
1331
|
-
sessionPath: latestSessionDir ?
|
|
1332
|
-
sessionLogPath: latestLogsDir ?
|
|
1703
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1704
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1333
1705
|
branch: runtimeId
|
|
1334
1706
|
});
|
|
1335
1707
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1337,8 +1709,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1337
1709
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1338
1710
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1339
1711
|
if (readyFile) {
|
|
1340
|
-
|
|
1341
|
-
|
|
1712
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1713
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1342
1714
|
`, "utf8");
|
|
1343
1715
|
}
|
|
1344
1716
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1352,7 +1724,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1352
1724
|
});
|
|
1353
1725
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1354
1726
|
}
|
|
1355
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1727
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1356
1728
|
snapshotSidecarPromise = (async () => {
|
|
1357
1729
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1358
1730
|
projectRoot: context.projectRoot,
|
|
@@ -1434,9 +1806,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1434
1806
|
}
|
|
1435
1807
|
return true;
|
|
1436
1808
|
}
|
|
1809
|
+
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") {
|
|
1810
|
+
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";
|
|
1811
|
+
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");
|
|
1812
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1813
|
+
id: nextRunLogId(),
|
|
1814
|
+
title,
|
|
1815
|
+
detail,
|
|
1816
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1817
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1818
|
+
payload: {
|
|
1819
|
+
eventType: event.type,
|
|
1820
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1821
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1822
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1823
|
+
},
|
|
1824
|
+
createdAt: new Date().toISOString()
|
|
1825
|
+
});
|
|
1826
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1827
|
+
return true;
|
|
1828
|
+
}
|
|
1829
|
+
if (event.type === "pi.session.ready") {
|
|
1830
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1831
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1832
|
+
if (privateMetadata) {
|
|
1833
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1834
|
+
piSession: publicMetadata,
|
|
1835
|
+
piSessionPrivate: privateMetadata
|
|
1836
|
+
});
|
|
1837
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1838
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1839
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1840
|
+
title: "Worker Pi session ready",
|
|
1841
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1842
|
+
tone: "info",
|
|
1843
|
+
status: "running",
|
|
1844
|
+
payload: {
|
|
1845
|
+
sessionId: sessionId || null,
|
|
1846
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1847
|
+
},
|
|
1848
|
+
createdAt: new Date().toISOString()
|
|
1849
|
+
});
|
|
1850
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1851
|
+
}
|
|
1852
|
+
return true;
|
|
1853
|
+
}
|
|
1854
|
+
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") {
|
|
1855
|
+
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";
|
|
1856
|
+
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")}`;
|
|
1857
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1858
|
+
id: nextRunLogId(),
|
|
1859
|
+
title,
|
|
1860
|
+
detail,
|
|
1861
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1862
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1863
|
+
payload,
|
|
1864
|
+
createdAt: new Date().toISOString()
|
|
1865
|
+
});
|
|
1866
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1437
1869
|
return false;
|
|
1438
1870
|
};
|
|
1439
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1440
1871
|
const handleAgentStdoutLine = (line) => {
|
|
1441
1872
|
const trimmed = line.trim();
|
|
1442
1873
|
if (!trimmed)
|
|
@@ -1470,6 +1901,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1470
1901
|
try {
|
|
1471
1902
|
const record = JSON.parse(trimmed);
|
|
1472
1903
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1904
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1905
|
+
projectRoot: context.projectRoot,
|
|
1906
|
+
runId: input.runId,
|
|
1907
|
+
record,
|
|
1908
|
+
status: liveLogStatus,
|
|
1909
|
+
nextRunLogId
|
|
1910
|
+
})) {
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1914
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1473
1917
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1474
1918
|
runId: input.runId,
|
|
1475
1919
|
record,
|
|
@@ -1486,7 +1930,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1486
1930
|
if (providerLogs.length > 0) {
|
|
1487
1931
|
for (const providerLog of providerLogs) {
|
|
1488
1932
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
1933
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1489
1934
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
1935
|
+
if (providerLog.title === "Tool activity")
|
|
1936
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1490
1937
|
}
|
|
1491
1938
|
}
|
|
1492
1939
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1538,6 +1985,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1538
1985
|
return;
|
|
1539
1986
|
}
|
|
1540
1987
|
}
|
|
1988
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1541
1991
|
if (record.type === "assistant") {
|
|
1542
1992
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1543
1993
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1574,7 +2024,18 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1574
2024
|
let reviewFailureDetail = null;
|
|
1575
2025
|
const stderrLines = [];
|
|
1576
2026
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
1577
|
-
if (resumeMode &&
|
|
2027
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
2028
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2029
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
2030
|
+
title: "Resume continuing from closeout phase",
|
|
2031
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
2032
|
+
tone: "info",
|
|
2033
|
+
status: resumePreviousStatus,
|
|
2034
|
+
createdAt: new Date().toISOString()
|
|
2035
|
+
});
|
|
2036
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
2037
|
+
exit = { code: 0, signal: null };
|
|
2038
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
1578
2039
|
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
1579
2040
|
taskId: input.taskId ?? runtimeTaskId,
|
|
1580
2041
|
workspaceDir: latestRuntimeWorkspace
|
|
@@ -1633,7 +2094,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1633
2094
|
let acceptedArtifactObservedAt = null;
|
|
1634
2095
|
let acceptedArtifactPollTimer = null;
|
|
1635
2096
|
let acceptedArtifactKillTimer = null;
|
|
1636
|
-
const attemptExit = await new Promise((
|
|
2097
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1637
2098
|
let settled = false;
|
|
1638
2099
|
const settle = (result) => {
|
|
1639
2100
|
if (settled)
|
|
@@ -1641,7 +2102,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1641
2102
|
settled = true;
|
|
1642
2103
|
if (acceptedArtifactPollTimer)
|
|
1643
2104
|
clearInterval(acceptedArtifactPollTimer);
|
|
1644
|
-
|
|
2105
|
+
resolve7(result);
|
|
1645
2106
|
};
|
|
1646
2107
|
const pollAcceptedArtifacts = () => {
|
|
1647
2108
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1714,7 +2175,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1714
2175
|
});
|
|
1715
2176
|
for (const pendingLog of pendingLogs) {
|
|
1716
2177
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2178
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1717
2179
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2180
|
+
if (pendingLog.title === "Tool activity")
|
|
2181
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1718
2182
|
}
|
|
1719
2183
|
process.off("SIGTERM", forwardSigterm);
|
|
1720
2184
|
if (attemptExit.error) {
|
|
@@ -1840,8 +2304,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1840
2304
|
}
|
|
1841
2305
|
if (planningClassification.planningRequired) {
|
|
1842
2306
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1843
|
-
const expectedPlanPath =
|
|
1844
|
-
if (!
|
|
2307
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2308
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1845
2309
|
const failedAt = new Date().toISOString();
|
|
1846
2310
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1847
2311
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1861,6 +2325,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1861
2325
|
throw new CliError2(failureDetail, 1);
|
|
1862
2326
|
}
|
|
1863
2327
|
}
|
|
2328
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2329
|
+
appendPiStageLog({
|
|
2330
|
+
projectRoot: context.projectRoot,
|
|
2331
|
+
runId: input.runId,
|
|
2332
|
+
stage: "Validate",
|
|
2333
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2334
|
+
status: "completed"
|
|
2335
|
+
});
|
|
2336
|
+
if (verificationAction && !reviewStarted) {
|
|
2337
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2338
|
+
}
|
|
2339
|
+
if (!reviewAction) {
|
|
2340
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2341
|
+
}
|
|
2342
|
+
if (reviewAction) {
|
|
2343
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2344
|
+
}
|
|
2345
|
+
const requestedAt = new Date().toISOString();
|
|
2346
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2347
|
+
status: "reviewing",
|
|
2348
|
+
completedAt: null,
|
|
2349
|
+
errorText: null,
|
|
2350
|
+
serverCloseout: {
|
|
2351
|
+
status: "pending",
|
|
2352
|
+
phase: "queued",
|
|
2353
|
+
requestedAt,
|
|
2354
|
+
updatedAt: requestedAt,
|
|
2355
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2356
|
+
branch: latestRuntimeBranch,
|
|
2357
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2361
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2362
|
+
title: "Server-owned closeout requested",
|
|
2363
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2364
|
+
tone: "info",
|
|
2365
|
+
status: "reviewing",
|
|
2366
|
+
createdAt: requestedAt,
|
|
2367
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2368
|
+
});
|
|
2369
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2370
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2371
|
+
await context.emitEvent("command.finished", {
|
|
2372
|
+
command: [
|
|
2373
|
+
"rig",
|
|
2374
|
+
"server",
|
|
2375
|
+
"task-run",
|
|
2376
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2377
|
+
...input.title ? ["--title", input.title] : []
|
|
2378
|
+
],
|
|
2379
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2380
|
+
exitCode: 0,
|
|
2381
|
+
durationMs: 0,
|
|
2382
|
+
startedAt,
|
|
2383
|
+
finishedAt: requestedAt
|
|
2384
|
+
});
|
|
2385
|
+
process.exit(0);
|
|
2386
|
+
}
|
|
1864
2387
|
const runPiPrFeedbackFix = async (message) => {
|
|
1865
2388
|
appendPiStageLog({
|
|
1866
2389
|
projectRoot: context.projectRoot,
|
|
@@ -1888,11 +2411,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1888
2411
|
child.stdin.write(message);
|
|
1889
2412
|
}
|
|
1890
2413
|
child.stdin.end();
|
|
2414
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2415
|
+
let feedbackAssistantText = "";
|
|
2416
|
+
const feedbackPendingToolUses = new Map;
|
|
1891
2417
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1892
2418
|
stdout.on("line", (line) => {
|
|
1893
2419
|
const trimmed = line.trim();
|
|
1894
2420
|
if (!trimmed)
|
|
1895
2421
|
return;
|
|
2422
|
+
try {
|
|
2423
|
+
const record = JSON.parse(trimmed);
|
|
2424
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2425
|
+
runId: input.runId,
|
|
2426
|
+
record,
|
|
2427
|
+
createdAtFallback: new Date().toISOString(),
|
|
2428
|
+
status: "reviewing",
|
|
2429
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2430
|
+
});
|
|
2431
|
+
for (const providerLog of providerLogs) {
|
|
2432
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2433
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2434
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2435
|
+
if (providerLog.title === "Tool activity")
|
|
2436
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2437
|
+
}
|
|
2438
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2439
|
+
projectRoot: context.projectRoot,
|
|
2440
|
+
runId: input.runId,
|
|
2441
|
+
messageId: feedbackAssistantMessageId,
|
|
2442
|
+
record,
|
|
2443
|
+
assistantText: feedbackAssistantText
|
|
2444
|
+
});
|
|
2445
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2446
|
+
if (hadAssistantDelta) {
|
|
2447
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2448
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2449
|
+
}
|
|
2450
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2451
|
+
return;
|
|
2452
|
+
} catch {}
|
|
1896
2453
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1897
2454
|
id: nextRunLogId(),
|
|
1898
2455
|
title: "Pi PR feedback fix output",
|
|
@@ -1918,10 +2475,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1918
2475
|
});
|
|
1919
2476
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1920
2477
|
});
|
|
1921
|
-
const exitCode = await new Promise((
|
|
1922
|
-
child.once("error", () =>
|
|
1923
|
-
child.once("close", (code) =>
|
|
2478
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2479
|
+
child.once("error", () => resolve7(1));
|
|
2480
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1924
2481
|
});
|
|
2482
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2483
|
+
runId: input.runId,
|
|
2484
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2485
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2486
|
+
})) {
|
|
2487
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2488
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2489
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2490
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2491
|
+
}
|
|
2492
|
+
if (feedbackAssistantText.trim()) {
|
|
2493
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2494
|
+
id: feedbackAssistantMessageId,
|
|
2495
|
+
type: "assistant_message",
|
|
2496
|
+
text: feedbackAssistantText,
|
|
2497
|
+
state: "completed",
|
|
2498
|
+
createdAt: new Date().toISOString(),
|
|
2499
|
+
completedAt: new Date().toISOString()
|
|
2500
|
+
});
|
|
2501
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2502
|
+
}
|
|
1925
2503
|
if (exitCode !== 0) {
|
|
1926
2504
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1927
2505
|
}
|