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