@h-rig/cli 0.0.6-alpha.6 → 0.0.6-alpha.60
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 +4184 -1228
- 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 +34 -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 +710 -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 +4143 -1181
- package/dist/src/index.js +4156 -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 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/task-run-driver.ts
|
|
3
|
-
import { copyFileSync, existsSync as
|
|
4
|
-
import { resolve as
|
|
3
|
+
import { copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
4
|
+
import { resolve as resolve6 } from "path";
|
|
5
5
|
import { spawn, spawnSync } from "child_process";
|
|
6
6
|
import { createInterface as createLineInterface } from "readline";
|
|
7
7
|
|
|
@@ -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
|
|
|
@@ -55,11 +53,13 @@ import { readFileSync } from "fs";
|
|
|
55
53
|
import { resolve as resolve3 } from "path";
|
|
56
54
|
import {
|
|
57
55
|
appendJsonlRecord,
|
|
56
|
+
appendRunLifecycleEvent,
|
|
58
57
|
readAuthorityRun as readAuthorityRun2,
|
|
59
58
|
resolveAuthorityRunDir as resolveAuthorityRunDir2,
|
|
60
59
|
writeJsonFile as writeJsonFile2
|
|
61
60
|
} from "@rig/runtime/control-plane/authority-files";
|
|
62
61
|
import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
|
|
62
|
+
import { readPriorPrProgressPromptSection } from "@rig/runtime/control-plane/prompt-context";
|
|
63
63
|
import { buildProviderTaskRunInstructionLines } from "@rig/runtime/control-plane/provider/runtime-instructions";
|
|
64
64
|
import { loadRigTaskRunSkillBody } from "@rig/runtime/control-plane/provider/rig-task-run-skill";
|
|
65
65
|
|
|
@@ -259,8 +259,38 @@ function startRunAction(projectRoot, runId, input) {
|
|
|
259
259
|
}
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
|
+
var runEventSinkProjectRoot = null;
|
|
263
|
+
function configureRunEventSink(projectRoot) {
|
|
264
|
+
runEventSinkProjectRoot = projectRoot;
|
|
265
|
+
}
|
|
266
|
+
var lastDoorbellRangAt = 0;
|
|
267
|
+
function ringServerRunDoorbell(runId) {
|
|
268
|
+
const serverUrl = process.env.RIG_SERVER_URL?.trim();
|
|
269
|
+
if (!serverUrl)
|
|
270
|
+
return;
|
|
271
|
+
const now = Date.now();
|
|
272
|
+
if (now - lastDoorbellRangAt < 250)
|
|
273
|
+
return;
|
|
274
|
+
lastDoorbellRangAt = now;
|
|
275
|
+
const token = process.env.RIG_AUTH_TOKEN || process.env.RIG_GITHUB_TOKEN || "";
|
|
276
|
+
fetch(`${serverUrl.replace(/\/+$/, "")}/api/runs/doorbell`, {
|
|
277
|
+
method: "POST",
|
|
278
|
+
headers: {
|
|
279
|
+
"content-type": "application/json",
|
|
280
|
+
authorization: `Bearer ${token}`
|
|
281
|
+
},
|
|
282
|
+
body: JSON.stringify({ runId }),
|
|
283
|
+
signal: AbortSignal.timeout(1000)
|
|
284
|
+
}).catch(() => {});
|
|
285
|
+
}
|
|
262
286
|
function emitServerRunEvent(event) {
|
|
263
287
|
console.log(`__RIG_RUN_EVENT__${JSON.stringify({ ...event, at: new Date().toISOString() })}`);
|
|
288
|
+
if (runEventSinkProjectRoot) {
|
|
289
|
+
try {
|
|
290
|
+
appendRunLifecycleEvent(runEventSinkProjectRoot, event.runId, { ...event });
|
|
291
|
+
} catch {}
|
|
292
|
+
ringServerRunDoorbell(event.runId);
|
|
293
|
+
}
|
|
264
294
|
}
|
|
265
295
|
function buildRunPrompt(input) {
|
|
266
296
|
const initialPrompt = input.initialPrompt?.trim() || null;
|
|
@@ -317,6 +347,7 @@ ${acceptance}` : null,
|
|
|
317
347
|
${sourceContractLines.join(`
|
|
318
348
|
`)}` : null,
|
|
319
349
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
350
|
+
readPriorPrProgressPromptSection(input.projectRoot, input.taskId),
|
|
320
351
|
initialPrompt ? `Additional operator guidance:
|
|
321
352
|
${initialPrompt}` : null,
|
|
322
353
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -388,6 +419,253 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
388
419
|
`);
|
|
389
420
|
}
|
|
390
421
|
|
|
422
|
+
// packages/cli/src/commands/_server-client.ts
|
|
423
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
424
|
+
import { resolve as resolve5 } from "path";
|
|
425
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
426
|
+
|
|
427
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
428
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
429
|
+
import { homedir } from "os";
|
|
430
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
431
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
432
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
433
|
+
if (explicit)
|
|
434
|
+
return resolve4(explicit);
|
|
435
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
436
|
+
if (stateDir)
|
|
437
|
+
return resolve4(stateDir, "connections.json");
|
|
438
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
439
|
+
}
|
|
440
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
441
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
442
|
+
}
|
|
443
|
+
function readJsonFile(path) {
|
|
444
|
+
if (!existsSync2(path))
|
|
445
|
+
return null;
|
|
446
|
+
try {
|
|
447
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
448
|
+
} catch (error) {
|
|
449
|
+
throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function writeJsonFile3(path, value) {
|
|
453
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
454
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
455
|
+
`, "utf8");
|
|
456
|
+
}
|
|
457
|
+
function normalizeConnection(value) {
|
|
458
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
459
|
+
return null;
|
|
460
|
+
const record = value;
|
|
461
|
+
if (record.kind === "local")
|
|
462
|
+
return { kind: "local", mode: "auto" };
|
|
463
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
464
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
465
|
+
return { kind: "remote", baseUrl };
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
function readGlobalConnections(options = {}) {
|
|
470
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
471
|
+
const payload = readJsonFile(path);
|
|
472
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
473
|
+
return { connections: {} };
|
|
474
|
+
}
|
|
475
|
+
const rawConnections = payload.connections;
|
|
476
|
+
const connections = {};
|
|
477
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
478
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
479
|
+
const connection = normalizeConnection(raw);
|
|
480
|
+
if (connection)
|
|
481
|
+
connections[alias] = connection;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return { connections };
|
|
485
|
+
}
|
|
486
|
+
function readRepoConnection(projectRoot) {
|
|
487
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
488
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
489
|
+
return null;
|
|
490
|
+
const record = payload;
|
|
491
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
492
|
+
if (!selected)
|
|
493
|
+
return null;
|
|
494
|
+
return {
|
|
495
|
+
selected,
|
|
496
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
497
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
498
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function writeRepoConnection(projectRoot, state) {
|
|
502
|
+
writeJsonFile3(resolveRepoConnectionPath(projectRoot), state);
|
|
503
|
+
}
|
|
504
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
505
|
+
const repo = readRepoConnection(projectRoot);
|
|
506
|
+
if (!repo)
|
|
507
|
+
return null;
|
|
508
|
+
if (repo.selected === "local")
|
|
509
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
510
|
+
const global = readGlobalConnections(options);
|
|
511
|
+
const connection = global.connections[repo.selected];
|
|
512
|
+
if (!connection) {
|
|
513
|
+
throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
514
|
+
}
|
|
515
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
516
|
+
}
|
|
517
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
518
|
+
const repo = readRepoConnection(projectRoot);
|
|
519
|
+
if (!repo)
|
|
520
|
+
return;
|
|
521
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// packages/cli/src/commands/_server-client.ts
|
|
525
|
+
var scopedGitHubBearerTokens = new Map;
|
|
526
|
+
function cleanToken(value) {
|
|
527
|
+
const trimmed = value?.trim();
|
|
528
|
+
return trimmed ? trimmed : null;
|
|
529
|
+
}
|
|
530
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
531
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
532
|
+
if (!existsSync3(path))
|
|
533
|
+
return null;
|
|
534
|
+
try {
|
|
535
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
536
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
537
|
+
} catch {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
542
|
+
const scopedKey = resolve5(projectRoot);
|
|
543
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
544
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
545
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
546
|
+
if (privateSession)
|
|
547
|
+
return privateSession;
|
|
548
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
549
|
+
}
|
|
550
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
551
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
552
|
+
if (!existsSync3(path))
|
|
553
|
+
return null;
|
|
554
|
+
try {
|
|
555
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
556
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
557
|
+
} catch {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
562
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
563
|
+
}
|
|
564
|
+
async function ensureServerForCli(projectRoot) {
|
|
565
|
+
try {
|
|
566
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
567
|
+
if (selected?.connection.kind === "remote") {
|
|
568
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
569
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
570
|
+
return {
|
|
571
|
+
baseUrl: selected.connection.baseUrl,
|
|
572
|
+
authToken,
|
|
573
|
+
connectionKind: "remote",
|
|
574
|
+
serverProjectRoot
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
578
|
+
return {
|
|
579
|
+
baseUrl: connection.baseUrl,
|
|
580
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
581
|
+
connectionKind: "local",
|
|
582
|
+
serverProjectRoot: resolve5(projectRoot)
|
|
583
|
+
};
|
|
584
|
+
} catch (error) {
|
|
585
|
+
if (error instanceof Error) {
|
|
586
|
+
throw new CliError2(error.message, 1);
|
|
587
|
+
}
|
|
588
|
+
throw error;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
592
|
+
const repo = readRepoConnection(projectRoot);
|
|
593
|
+
const slug = repo?.project?.trim();
|
|
594
|
+
if (!slug)
|
|
595
|
+
return null;
|
|
596
|
+
try {
|
|
597
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
598
|
+
headers: mergeHeaders(undefined, authToken)
|
|
599
|
+
});
|
|
600
|
+
if (!response.ok)
|
|
601
|
+
return null;
|
|
602
|
+
const payload = await response.json();
|
|
603
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
604
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
605
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
606
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
607
|
+
if (path)
|
|
608
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
609
|
+
return path;
|
|
610
|
+
} catch {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function mergeHeaders(headers, authToken) {
|
|
615
|
+
const merged = new Headers(headers);
|
|
616
|
+
if (authToken) {
|
|
617
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
618
|
+
}
|
|
619
|
+
return merged;
|
|
620
|
+
}
|
|
621
|
+
function diagnosticMessage(payload) {
|
|
622
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
623
|
+
return null;
|
|
624
|
+
const record = payload;
|
|
625
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
626
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
627
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
628
|
+
return [];
|
|
629
|
+
const diagnostic = entry;
|
|
630
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
631
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
632
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
633
|
+
});
|
|
634
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
635
|
+
}
|
|
636
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
637
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
638
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
639
|
+
if (server.serverProjectRoot)
|
|
640
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
641
|
+
const response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
642
|
+
...init,
|
|
643
|
+
headers
|
|
644
|
+
});
|
|
645
|
+
const text = await response.text();
|
|
646
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
647
|
+
try {
|
|
648
|
+
return JSON.parse(text);
|
|
649
|
+
} catch {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
})() : null;
|
|
653
|
+
if (!response.ok) {
|
|
654
|
+
const diagnostics = diagnosticMessage(payload);
|
|
655
|
+
const detail = diagnostics ?? (text || response.statusText);
|
|
656
|
+
throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
|
|
657
|
+
}
|
|
658
|
+
return payload;
|
|
659
|
+
}
|
|
660
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
661
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
662
|
+
method: "POST",
|
|
663
|
+
headers: { "content-type": "application/json" },
|
|
664
|
+
body: JSON.stringify(input)
|
|
665
|
+
});
|
|
666
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
667
|
+
}
|
|
668
|
+
|
|
391
669
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
670
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
671
|
"Connect",
|
|
@@ -431,6 +709,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
709
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
710
|
RIG_TASK_ID: input.taskId,
|
|
433
711
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
712
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
713
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
714
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
715
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +734,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
734
|
return 0;
|
|
456
735
|
let copied = 0;
|
|
457
736
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
737
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
738
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
739
|
try {
|
|
461
740
|
if (!statSync(sourcePath).isFile())
|
|
462
741
|
continue;
|
|
463
|
-
|
|
742
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
743
|
copyFileSync(sourcePath, targetPath);
|
|
465
744
|
copied += 1;
|
|
466
745
|
} catch {}
|
|
@@ -499,7 +778,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
778
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
779
|
return {
|
|
501
780
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
781
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
782
|
};
|
|
504
783
|
}
|
|
505
784
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +786,7 @@ function positiveInt(value, fallback) {
|
|
|
507
786
|
}
|
|
508
787
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
788
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
789
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
790
|
return {
|
|
512
791
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
792
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +880,6 @@ function appendPiStageLog(input) {
|
|
|
601
880
|
});
|
|
602
881
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
882
|
}
|
|
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
883
|
function createCommandRunner(binary) {
|
|
611
884
|
return async (args, options) => {
|
|
612
885
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +890,9 @@ function createCommandRunner(binary) {
|
|
|
617
890
|
const stderrChunks = [];
|
|
618
891
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
892
|
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) =>
|
|
893
|
+
return await new Promise((resolve7) => {
|
|
894
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
895
|
+
child.once("close", (code) => resolve7({
|
|
623
896
|
exitCode: code ?? 1,
|
|
624
897
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
898
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +916,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
916
|
if (!taskId) {
|
|
644
917
|
return { status: "skipped" };
|
|
645
918
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
919
|
+
const configInput = input.config ?? null;
|
|
920
|
+
const config = configInput ?? {};
|
|
921
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
922
|
if (prMode === "off" || prMode === "ask") {
|
|
649
923
|
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
924
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +934,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
934
|
gitCommand
|
|
661
935
|
});
|
|
662
936
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
937
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
938
|
const stage = input.appendStage ?? (() => {
|
|
665
939
|
return;
|
|
666
940
|
});
|
|
@@ -682,7 +956,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
956
|
command: gitCommand
|
|
683
957
|
});
|
|
684
958
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
959
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
960
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
961
|
const pr = await prAutomation({
|
|
688
962
|
projectRoot: workspace,
|
|
@@ -692,7 +966,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
966
|
config,
|
|
693
967
|
sourceTask: input.sourceTask,
|
|
694
968
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
969
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
970
|
command: ghCommand,
|
|
971
|
+
gitCommand,
|
|
696
972
|
steerPi,
|
|
697
973
|
lifecycle: {
|
|
698
974
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +1017,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
1017
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
1018
|
})
|
|
743
1019
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
1020
|
});
|
|
747
1021
|
}
|
|
748
1022
|
},
|
|
@@ -761,8 +1035,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
1035
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
1036
|
})
|
|
763
1037
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
1038
|
});
|
|
767
1039
|
}
|
|
768
1040
|
},
|
|
@@ -791,8 +1063,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
1063
|
errorText: detail
|
|
792
1064
|
})
|
|
793
1065
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
1066
|
+
}).catch((error) => {
|
|
1067
|
+
try {
|
|
1068
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1069
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
1070
|
+
title: "Task source needs-attention update failed",
|
|
1071
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1072
|
+
tone: "error",
|
|
1073
|
+
status: "needs_attention",
|
|
1074
|
+
createdAt: new Date().toISOString()
|
|
1075
|
+
});
|
|
1076
|
+
} catch {}
|
|
796
1077
|
});
|
|
797
1078
|
}
|
|
798
1079
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1096,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1096
|
return null;
|
|
816
1097
|
}
|
|
817
1098
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1099
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1100
|
if (!summary || summary.status !== "fail") {
|
|
820
1101
|
continue;
|
|
821
1102
|
}
|
|
@@ -896,14 +1177,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1177
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1178
|
return { accepted: false, reason: null };
|
|
898
1179
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1180
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1181
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1182
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1183
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1184
|
if (reviewStatus !== "APPROVED") {
|
|
904
1185
|
return { accepted: false, reason: null };
|
|
905
1186
|
}
|
|
906
|
-
const taskResult =
|
|
1187
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1188
|
if (taskResult?.status !== "completed") {
|
|
908
1189
|
return { accepted: false, reason: null };
|
|
909
1190
|
}
|
|
@@ -935,13 +1216,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1216
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1217
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1218
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1219
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1220
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1221
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1222
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1223
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1224
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1225
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1226
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1227
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1228
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1275,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1275
|
}
|
|
995
1276
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1277
|
}
|
|
1278
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1279
|
+
let nextAssistantText = input.assistantText;
|
|
1280
|
+
if (input.record.type === "message_update") {
|
|
1281
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1282
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1283
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1284
|
+
}
|
|
1285
|
+
} else if (input.record.type === "stream_event") {
|
|
1286
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1287
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1288
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1289
|
+
nextAssistantText += delta.text;
|
|
1290
|
+
}
|
|
1291
|
+
} else if (input.record.type === "assistant") {
|
|
1292
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1293
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1294
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1295
|
+
if (fullText.length > nextAssistantText.length)
|
|
1296
|
+
nextAssistantText = fullText;
|
|
1297
|
+
}
|
|
1298
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1299
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1300
|
+
id: input.messageId,
|
|
1301
|
+
type: "assistant_message",
|
|
1302
|
+
text: nextAssistantText,
|
|
1303
|
+
state: "streaming",
|
|
1304
|
+
createdAt: new Date().toISOString()
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
return nextAssistantText;
|
|
1308
|
+
}
|
|
1309
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1310
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1311
|
+
if (type === "response") {
|
|
1312
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1313
|
+
const success = input.record.success !== false;
|
|
1314
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1318
|
+
id: input.nextRunLogId(),
|
|
1319
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1320
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1321
|
+
tone: success ? "tool" : "error",
|
|
1322
|
+
status: input.status,
|
|
1323
|
+
payload: input.record,
|
|
1324
|
+
createdAt: new Date().toISOString()
|
|
1325
|
+
});
|
|
1326
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1327
|
+
return true;
|
|
1328
|
+
}
|
|
1329
|
+
if (type !== "extension_ui_request")
|
|
1330
|
+
return false;
|
|
1331
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1332
|
+
let title = "Pi UI event";
|
|
1333
|
+
let detail = method;
|
|
1334
|
+
let tone = "info";
|
|
1335
|
+
if (method === "notify") {
|
|
1336
|
+
title = "Pi notification";
|
|
1337
|
+
detail = String(input.record.message ?? "");
|
|
1338
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1339
|
+
} else if (method === "setStatus") {
|
|
1340
|
+
title = "Pi UI status";
|
|
1341
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1342
|
+
tone = "tool";
|
|
1343
|
+
} else if (method === "setWidget") {
|
|
1344
|
+
title = "Pi UI widget";
|
|
1345
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1346
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1347
|
+
tone = "tool";
|
|
1348
|
+
} else if (method === "setTitle") {
|
|
1349
|
+
title = "Pi UI title";
|
|
1350
|
+
detail = String(input.record.title ?? "");
|
|
1351
|
+
tone = "tool";
|
|
1352
|
+
} else if (method === "set_editor_text") {
|
|
1353
|
+
title = "Pi editor update";
|
|
1354
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1355
|
+
tone = "tool";
|
|
1356
|
+
} else {
|
|
1357
|
+
title = "Pi UI request";
|
|
1358
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1359
|
+
}
|
|
1360
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1361
|
+
id: input.nextRunLogId(),
|
|
1362
|
+
title,
|
|
1363
|
+
detail,
|
|
1364
|
+
tone,
|
|
1365
|
+
status: input.status,
|
|
1366
|
+
payload: input.record,
|
|
1367
|
+
createdAt: new Date().toISOString()
|
|
1368
|
+
});
|
|
1369
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1370
|
+
return true;
|
|
1371
|
+
}
|
|
1372
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1373
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1374
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1375
|
+
return false;
|
|
1376
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1377
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1378
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1379
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1380
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1381
|
+
type,
|
|
1382
|
+
toolName,
|
|
1383
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1384
|
+
createdAt: new Date().toISOString()
|
|
1385
|
+
});
|
|
1386
|
+
return true;
|
|
1387
|
+
}
|
|
1388
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1389
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1390
|
+
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");
|
|
1391
|
+
}
|
|
1392
|
+
function appendToolTimelineFromLog(input) {
|
|
1393
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1394
|
+
if (title !== "Tool activity")
|
|
1395
|
+
return;
|
|
1396
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1397
|
+
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;
|
|
1398
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1399
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1400
|
+
id: `tool:${logId}`,
|
|
1401
|
+
type: "tool_execution_update",
|
|
1402
|
+
toolName,
|
|
1403
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1404
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1405
|
+
payload,
|
|
1406
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
997
1409
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1410
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1411
|
return null;
|
|
1000
1412
|
}
|
|
1001
1413
|
try {
|
|
1002
|
-
const status =
|
|
1414
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1415
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1416
|
} catch {
|
|
1005
1417
|
return null;
|
|
@@ -1017,7 +1429,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1429
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1430
|
return sourceTask;
|
|
1019
1431
|
}
|
|
1020
|
-
|
|
1432
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1433
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1434
|
+
return null;
|
|
1435
|
+
const record = sourceTask;
|
|
1436
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1437
|
+
if (direct?.trim())
|
|
1438
|
+
return direct.trim();
|
|
1439
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1440
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1441
|
+
}
|
|
1442
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1443
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1444
|
+
id: input.taskId,
|
|
1445
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1446
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1447
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1448
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1449
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1450
|
+
});
|
|
1451
|
+
if (serverResult.ok === false) {
|
|
1452
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1453
|
+
}
|
|
1454
|
+
return {
|
|
1455
|
+
updated: serverResult.ok !== false,
|
|
1456
|
+
taskId: input.taskId,
|
|
1457
|
+
status: input.update.status,
|
|
1458
|
+
source: "server",
|
|
1459
|
+
sourceKind: "server",
|
|
1460
|
+
projectSync: serverResult.projectSync
|
|
1461
|
+
};
|
|
1462
|
+
};
|
|
1463
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1464
|
if (!taskId)
|
|
1022
1465
|
return;
|
|
1023
1466
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1081,6 +1524,9 @@ function stringArrayField(record, key) {
|
|
|
1081
1524
|
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
1082
1525
|
}
|
|
1083
1526
|
async function executeRigOwnedTaskRun(context, input) {
|
|
1527
|
+
process.stdout.on("error", () => {});
|
|
1528
|
+
process.stderr.on("error", () => {});
|
|
1529
|
+
configureRunEventSink(context.projectRoot);
|
|
1084
1530
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
1085
1531
|
const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
|
|
1086
1532
|
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
@@ -1107,11 +1553,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1107
1553
|
...input.model ? ["--model", input.model] : [],
|
|
1108
1554
|
"--prompt"
|
|
1109
1555
|
] : input.runtimeAdapter === "pi" ? [
|
|
1110
|
-
"
|
|
1111
|
-
"--verbose",
|
|
1112
|
-
"--mode",
|
|
1113
|
-
"json",
|
|
1114
|
-
"--no-session",
|
|
1556
|
+
"__rig_pi_session_daemon__",
|
|
1115
1557
|
...input.model ? ["--model", input.model] : []
|
|
1116
1558
|
] : [
|
|
1117
1559
|
"--print",
|
|
@@ -1186,15 +1628,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1186
1628
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1187
1629
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1188
1630
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1189
|
-
const planningArtifactPath =
|
|
1631
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1190
1632
|
const persistedPlanning = {
|
|
1191
1633
|
...planningClassification,
|
|
1192
1634
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1193
1635
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1194
1636
|
classifiedAt: new Date().toISOString()
|
|
1195
1637
|
};
|
|
1196
|
-
|
|
1197
|
-
|
|
1638
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1639
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1198
1640
|
`, "utf8");
|
|
1199
1641
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1200
1642
|
prompt = `${prompt}
|
|
@@ -1208,7 +1650,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1208
1650
|
projectRoot: context.projectRoot,
|
|
1209
1651
|
runId: input.runId,
|
|
1210
1652
|
stage,
|
|
1211
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1653
|
+
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
1654
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1213
1655
|
});
|
|
1214
1656
|
}
|
|
@@ -1244,11 +1686,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1244
1686
|
let verificationStarted = false;
|
|
1245
1687
|
let reviewStarted = false;
|
|
1246
1688
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1247
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1689
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1248
1690
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1249
1691
|
let latestProviderCommand = null;
|
|
1250
1692
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1251
1693
|
let snapshotSidecarPromise = null;
|
|
1694
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1252
1695
|
let dirtyBaselineApplied = false;
|
|
1253
1696
|
const childEnv = {
|
|
1254
1697
|
...process.env,
|
|
@@ -1301,6 +1744,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1301
1744
|
detail: detail ?? "Verifier review is running."
|
|
1302
1745
|
});
|
|
1303
1746
|
};
|
|
1747
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1304
1748
|
const handleWrapperEvent = (rawPayload) => {
|
|
1305
1749
|
let event = null;
|
|
1306
1750
|
try {
|
|
@@ -1316,6 +1760,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1316
1760
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1317
1761
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1318
1762
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1763
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1319
1764
|
latestRuntimeBranch = runtimeId;
|
|
1320
1765
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1321
1766
|
runtimeId: runtimeId ?? null,
|
|
@@ -1327,10 +1772,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1327
1772
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1328
1773
|
status: "running",
|
|
1329
1774
|
worktreePath: latestRuntimeWorkspace,
|
|
1330
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1775
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1331
1776
|
logRoot: latestLogsDir,
|
|
1332
|
-
sessionPath: latestSessionDir ?
|
|
1333
|
-
sessionLogPath: latestLogsDir ?
|
|
1777
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1778
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1334
1779
|
branch: runtimeId
|
|
1335
1780
|
});
|
|
1336
1781
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1338,8 +1783,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1338
1783
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1339
1784
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1340
1785
|
if (readyFile) {
|
|
1341
|
-
|
|
1342
|
-
|
|
1786
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1787
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1343
1788
|
`, "utf8");
|
|
1344
1789
|
}
|
|
1345
1790
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1353,7 +1798,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1353
1798
|
});
|
|
1354
1799
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1355
1800
|
}
|
|
1356
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1801
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1357
1802
|
snapshotSidecarPromise = (async () => {
|
|
1358
1803
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1359
1804
|
projectRoot: context.projectRoot,
|
|
@@ -1435,9 +1880,68 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1435
1880
|
}
|
|
1436
1881
|
return true;
|
|
1437
1882
|
}
|
|
1883
|
+
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") {
|
|
1884
|
+
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";
|
|
1885
|
+
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");
|
|
1886
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1887
|
+
id: nextRunLogId(),
|
|
1888
|
+
title,
|
|
1889
|
+
detail,
|
|
1890
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1891
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1892
|
+
payload: {
|
|
1893
|
+
eventType: event.type,
|
|
1894
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
1895
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
1896
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
1897
|
+
},
|
|
1898
|
+
createdAt: new Date().toISOString()
|
|
1899
|
+
});
|
|
1900
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
if (event.type === "pi.session.ready") {
|
|
1904
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
1905
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
1906
|
+
if (privateMetadata) {
|
|
1907
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1908
|
+
piSession: publicMetadata,
|
|
1909
|
+
piSessionPrivate: privateMetadata
|
|
1910
|
+
});
|
|
1911
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
1912
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1913
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
1914
|
+
title: "Worker Pi session ready",
|
|
1915
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
1916
|
+
tone: "info",
|
|
1917
|
+
status: "running",
|
|
1918
|
+
payload: {
|
|
1919
|
+
sessionId: sessionId || null,
|
|
1920
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
1921
|
+
},
|
|
1922
|
+
createdAt: new Date().toISOString()
|
|
1923
|
+
});
|
|
1924
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
1925
|
+
}
|
|
1926
|
+
return true;
|
|
1927
|
+
}
|
|
1928
|
+
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") {
|
|
1929
|
+
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";
|
|
1930
|
+
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")}`;
|
|
1931
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1932
|
+
id: nextRunLogId(),
|
|
1933
|
+
title,
|
|
1934
|
+
detail,
|
|
1935
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
1936
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
1937
|
+
payload,
|
|
1938
|
+
createdAt: new Date().toISOString()
|
|
1939
|
+
});
|
|
1940
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1941
|
+
return true;
|
|
1942
|
+
}
|
|
1438
1943
|
return false;
|
|
1439
1944
|
};
|
|
1440
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1441
1945
|
const handleAgentStdoutLine = (line) => {
|
|
1442
1946
|
const trimmed = line.trim();
|
|
1443
1947
|
if (!trimmed)
|
|
@@ -1471,6 +1975,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1471
1975
|
try {
|
|
1472
1976
|
const record = JSON.parse(trimmed);
|
|
1473
1977
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
1978
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
1979
|
+
projectRoot: context.projectRoot,
|
|
1980
|
+
runId: input.runId,
|
|
1981
|
+
record,
|
|
1982
|
+
status: liveLogStatus,
|
|
1983
|
+
nextRunLogId
|
|
1984
|
+
})) {
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
1988
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1474
1991
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1475
1992
|
runId: input.runId,
|
|
1476
1993
|
record,
|
|
@@ -1487,7 +2004,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1487
2004
|
if (providerLogs.length > 0) {
|
|
1488
2005
|
for (const providerLog of providerLogs) {
|
|
1489
2006
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2007
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1490
2008
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2009
|
+
if (providerLog.title === "Tool activity")
|
|
2010
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1491
2011
|
}
|
|
1492
2012
|
}
|
|
1493
2013
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1539,6 +2059,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1539
2059
|
return;
|
|
1540
2060
|
}
|
|
1541
2061
|
}
|
|
2062
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
1542
2065
|
if (record.type === "assistant") {
|
|
1543
2066
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1544
2067
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1645,7 +2168,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1645
2168
|
let acceptedArtifactObservedAt = null;
|
|
1646
2169
|
let acceptedArtifactPollTimer = null;
|
|
1647
2170
|
let acceptedArtifactKillTimer = null;
|
|
1648
|
-
const attemptExit = await new Promise((
|
|
2171
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1649
2172
|
let settled = false;
|
|
1650
2173
|
const settle = (result) => {
|
|
1651
2174
|
if (settled)
|
|
@@ -1653,7 +2176,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1653
2176
|
settled = true;
|
|
1654
2177
|
if (acceptedArtifactPollTimer)
|
|
1655
2178
|
clearInterval(acceptedArtifactPollTimer);
|
|
1656
|
-
|
|
2179
|
+
resolve7(result);
|
|
1657
2180
|
};
|
|
1658
2181
|
const pollAcceptedArtifacts = () => {
|
|
1659
2182
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1726,7 +2249,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1726
2249
|
});
|
|
1727
2250
|
for (const pendingLog of pendingLogs) {
|
|
1728
2251
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2252
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1729
2253
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2254
|
+
if (pendingLog.title === "Tool activity")
|
|
2255
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1730
2256
|
}
|
|
1731
2257
|
process.off("SIGTERM", forwardSigterm);
|
|
1732
2258
|
if (attemptExit.error) {
|
|
@@ -1852,8 +2378,8 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1852
2378
|
}
|
|
1853
2379
|
if (planningClassification.planningRequired) {
|
|
1854
2380
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1855
|
-
const expectedPlanPath =
|
|
1856
|
-
if (!
|
|
2381
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2382
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1857
2383
|
const failedAt = new Date().toISOString();
|
|
1858
2384
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1859
2385
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
@@ -1873,6 +2399,65 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1873
2399
|
throw new CliError2(failureDetail, 1);
|
|
1874
2400
|
}
|
|
1875
2401
|
}
|
|
2402
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2403
|
+
appendPiStageLog({
|
|
2404
|
+
projectRoot: context.projectRoot,
|
|
2405
|
+
runId: input.runId,
|
|
2406
|
+
stage: "Validate",
|
|
2407
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2408
|
+
status: "completed"
|
|
2409
|
+
});
|
|
2410
|
+
if (verificationAction && !reviewStarted) {
|
|
2411
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2412
|
+
}
|
|
2413
|
+
if (!reviewAction) {
|
|
2414
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
2415
|
+
}
|
|
2416
|
+
if (reviewAction) {
|
|
2417
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2418
|
+
}
|
|
2419
|
+
const requestedAt = new Date().toISOString();
|
|
2420
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2421
|
+
status: "reviewing",
|
|
2422
|
+
completedAt: null,
|
|
2423
|
+
errorText: null,
|
|
2424
|
+
serverCloseout: {
|
|
2425
|
+
status: "pending",
|
|
2426
|
+
phase: "queued",
|
|
2427
|
+
requestedAt,
|
|
2428
|
+
updatedAt: requestedAt,
|
|
2429
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2430
|
+
branch: latestRuntimeBranch,
|
|
2431
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2432
|
+
}
|
|
2433
|
+
});
|
|
2434
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2435
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2436
|
+
title: "Server-owned closeout requested",
|
|
2437
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2438
|
+
tone: "info",
|
|
2439
|
+
status: "reviewing",
|
|
2440
|
+
createdAt: requestedAt,
|
|
2441
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2442
|
+
});
|
|
2443
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2444
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: "Server-owned closeout requested." });
|
|
2445
|
+
await context.emitEvent("command.finished", {
|
|
2446
|
+
command: [
|
|
2447
|
+
"rig",
|
|
2448
|
+
"server",
|
|
2449
|
+
"task-run",
|
|
2450
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2451
|
+
...input.title ? ["--title", input.title] : []
|
|
2452
|
+
],
|
|
2453
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2454
|
+
exitCode: 0,
|
|
2455
|
+
durationMs: 0,
|
|
2456
|
+
startedAt,
|
|
2457
|
+
finishedAt: requestedAt
|
|
2458
|
+
});
|
|
2459
|
+
process.exit(0);
|
|
2460
|
+
}
|
|
1876
2461
|
const runPiPrFeedbackFix = async (message) => {
|
|
1877
2462
|
appendPiStageLog({
|
|
1878
2463
|
projectRoot: context.projectRoot,
|
|
@@ -1900,11 +2485,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1900
2485
|
child.stdin.write(message);
|
|
1901
2486
|
}
|
|
1902
2487
|
child.stdin.end();
|
|
2488
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2489
|
+
let feedbackAssistantText = "";
|
|
2490
|
+
const feedbackPendingToolUses = new Map;
|
|
1903
2491
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1904
2492
|
stdout.on("line", (line) => {
|
|
1905
2493
|
const trimmed = line.trim();
|
|
1906
2494
|
if (!trimmed)
|
|
1907
2495
|
return;
|
|
2496
|
+
try {
|
|
2497
|
+
const record = JSON.parse(trimmed);
|
|
2498
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2499
|
+
runId: input.runId,
|
|
2500
|
+
record,
|
|
2501
|
+
createdAtFallback: new Date().toISOString(),
|
|
2502
|
+
status: "reviewing",
|
|
2503
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2504
|
+
});
|
|
2505
|
+
for (const providerLog of providerLogs) {
|
|
2506
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2507
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2508
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2509
|
+
if (providerLog.title === "Tool activity")
|
|
2510
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2511
|
+
}
|
|
2512
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2513
|
+
projectRoot: context.projectRoot,
|
|
2514
|
+
runId: input.runId,
|
|
2515
|
+
messageId: feedbackAssistantMessageId,
|
|
2516
|
+
record,
|
|
2517
|
+
assistantText: feedbackAssistantText
|
|
2518
|
+
});
|
|
2519
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2520
|
+
if (hadAssistantDelta) {
|
|
2521
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2522
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2523
|
+
}
|
|
2524
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2525
|
+
return;
|
|
2526
|
+
} catch {}
|
|
1908
2527
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1909
2528
|
id: nextRunLogId(),
|
|
1910
2529
|
title: "Pi PR feedback fix output",
|
|
@@ -1930,10 +2549,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1930
2549
|
});
|
|
1931
2550
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1932
2551
|
});
|
|
1933
|
-
const exitCode = await new Promise((
|
|
1934
|
-
child.once("error", () =>
|
|
1935
|
-
child.once("close", (code) =>
|
|
2552
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2553
|
+
child.once("error", () => resolve7(1));
|
|
2554
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1936
2555
|
});
|
|
2556
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2557
|
+
runId: input.runId,
|
|
2558
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2559
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2560
|
+
})) {
|
|
2561
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2562
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2563
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2564
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2565
|
+
}
|
|
2566
|
+
if (feedbackAssistantText.trim()) {
|
|
2567
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2568
|
+
id: feedbackAssistantMessageId,
|
|
2569
|
+
type: "assistant_message",
|
|
2570
|
+
text: feedbackAssistantText,
|
|
2571
|
+
state: "completed",
|
|
2572
|
+
createdAt: new Date().toISOString(),
|
|
2573
|
+
completedAt: new Date().toISOString()
|
|
2574
|
+
});
|
|
2575
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2576
|
+
}
|
|
1937
2577
|
if (exitCode !== 0) {
|
|
1938
2578
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1939
2579
|
}
|