@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90
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/build-rig-binaries.d.ts +2 -0
- package/dist/bin/rig.d.ts +2 -0
- package/dist/bin/rig.js +11052 -6668
- package/dist/src/app/board.d.ts +23 -0
- package/dist/src/app/board.js +1786 -0
- package/dist/src/app/drone-ui.d.ts +37 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/app/theme.d.ts +47 -0
- package/dist/src/app/theme.js +150 -0
- package/dist/src/commands/_async-ui.d.ts +13 -0
- package/dist/src/commands/_async-ui.js +223 -0
- package/dist/src/commands/_authority-runs.d.ts +22 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.d.ts +49 -0
- package/dist/src/commands/_cli-format.js +447 -0
- package/dist/src/commands/_connection-state.d.ts +44 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.d.ts +52 -0
- package/dist/src/commands/_doctor-checks.js +180 -44
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +442 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.d.ts +34 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.d.ts +30 -0
- package/dist/src/commands/_operator-view.js +803 -73
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_pi-frontend.d.ts +27 -0
- package/dist/src/commands/_pi-frontend.js +742 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +5 -4
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.d.ts +22 -0
- package/dist/src/commands/_preflight.js +190 -128
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.d.ts +24 -0
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.d.ts +186 -0
- package/dist/src/commands/_server-client.js +360 -61
- package/dist/src/commands/_snapshot-upload.d.ts +39 -0
- package/dist/src/commands/_snapshot-upload.js +163 -39
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.d.ts +9 -0
- package/dist/src/commands/_task-picker.js +172 -19
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.d.ts +65 -0
- package/dist/src/commands/browser.js +334 -51
- package/dist/src/commands/connect.d.ts +7 -0
- package/dist/src/commands/connect.js +272 -33
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +378 -45
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +375 -53
- package/dist/src/commands/inbox.d.ts +30 -0
- package/dist/src/commands/inbox.js +760 -70
- package/dist/src/commands/init.d.ts +74 -0
- package/dist/src/commands/init.js +963 -119
- package/dist/src/commands/inspect.d.ts +3 -0
- package/dist/src/commands/inspect.js +588 -32
- package/dist/src/commands/inspector.d.ts +3 -0
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +16 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.d.ts +3 -0
- package/dist/src/commands/run.js +1379 -159
- package/dist/src/commands/server.d.ts +7 -0
- package/dist/src/commands/server.js +502 -57
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +390 -63
- package/dist/src/commands/stats.d.ts +12 -0
- package/dist/src/commands/stats.js +1051 -0
- package/dist/src/commands/task-report-bug.d.ts +19 -0
- package/dist/src/commands/task-report-bug.js +260 -62
- package/dist/src/commands/task-run-driver.d.ts +132 -0
- package/dist/src/commands/task-run-driver.js +886 -130
- package/dist/src/commands/task.d.ts +14 -0
- package/dist/src/commands/task.js +1644 -314
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +29 -0
- package/dist/src/commands.js +10967 -6637
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +10168 -5787
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.d.ts +44 -0
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +16 -22
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/package.json +13 -6
|
@@ -1,18 +1,25 @@
|
|
|
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
|
|
|
8
8
|
// packages/cli/src/runner.ts
|
|
9
9
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
10
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
10
|
+
import { CliError as RuntimeCliError } 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
|
+
|
|
14
|
+
class CliError extends RuntimeCliError {
|
|
15
|
+
hint;
|
|
16
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
17
|
+
super(message, exitCode);
|
|
18
|
+
if (options.hint?.trim()) {
|
|
19
|
+
this.hint = options.hint.trim();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
16
23
|
function formatCommand(parts) {
|
|
17
24
|
return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
|
|
18
25
|
}
|
|
@@ -39,14 +46,14 @@ import {
|
|
|
39
46
|
isCodexExecRecord
|
|
40
47
|
} from "@rig/runtime/control-plane/provider/codex-exec-records";
|
|
41
48
|
import { resolvePreferredShellBinary } from "@rig/runtime/control-plane/native/run-ops";
|
|
42
|
-
import { readAuthorityRun as readAuthorityRun3, readJsonFile, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
49
|
+
import { readAuthorityRun as readAuthorityRun3, readJsonFile as readJsonFile2, resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
43
50
|
import {
|
|
44
|
-
buildTaskRunLifecycleComment
|
|
45
|
-
updateConfiguredTaskSourceTask
|
|
51
|
+
buildTaskRunLifecycleComment
|
|
46
52
|
} from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
47
53
|
import {
|
|
48
54
|
closeIssueAfterMergedPr,
|
|
49
55
|
commitRunChanges,
|
|
56
|
+
pushBranchSyncedWithOrigin,
|
|
50
57
|
runPrAutomation
|
|
51
58
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
52
59
|
|
|
@@ -54,12 +61,17 @@ import {
|
|
|
54
61
|
import { readFileSync } from "fs";
|
|
55
62
|
import { resolve as resolve3 } from "path";
|
|
56
63
|
import {
|
|
57
|
-
|
|
64
|
+
appendRunJournalEvent,
|
|
65
|
+
appendRunLogEntry,
|
|
66
|
+
appendRunTimelineEntry,
|
|
67
|
+
patchAuthorityRunRecord,
|
|
58
68
|
readAuthorityRun as readAuthorityRun2,
|
|
59
|
-
resolveAuthorityRunDir
|
|
60
|
-
|
|
69
|
+
resolveAuthorityRunDir,
|
|
70
|
+
setAuthorityRunStatus,
|
|
71
|
+
writeJsonFile
|
|
61
72
|
} from "@rig/runtime/control-plane/authority-files";
|
|
62
73
|
import { taskLookup } from "@rig/runtime/control-plane/native/task-ops";
|
|
74
|
+
import { readPriorPrProgressPromptSection } from "@rig/runtime/control-plane/prompt-context";
|
|
63
75
|
import { buildProviderTaskRunInstructionLines } from "@rig/runtime/control-plane/provider/runtime-instructions";
|
|
64
76
|
import { loadRigTaskRunSkillBody } from "@rig/runtime/control-plane/provider/rig-task-run-skill";
|
|
65
77
|
|
|
@@ -69,8 +81,7 @@ import { resolve as resolve2 } from "path";
|
|
|
69
81
|
import {
|
|
70
82
|
readAuthorityRun,
|
|
71
83
|
readJsonlFile,
|
|
72
|
-
|
|
73
|
-
writeJsonFile
|
|
84
|
+
writeAuthorityRunRecord
|
|
74
85
|
} from "@rig/runtime/control-plane/authority-files";
|
|
75
86
|
|
|
76
87
|
// packages/cli/src/commands/_paths.ts
|
|
@@ -168,22 +179,26 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
168
179
|
} else if ("errorText" in next) {
|
|
169
180
|
delete next.errorText;
|
|
170
181
|
}
|
|
171
|
-
|
|
182
|
+
writeAuthorityRunRecord(projectRoot, input.runId, next);
|
|
172
183
|
return next;
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
176
187
|
function patchAuthorityRun(projectRoot, runId, patch) {
|
|
177
|
-
const
|
|
178
|
-
if (!
|
|
179
|
-
throw new
|
|
188
|
+
const next = patchAuthorityRunRecord(projectRoot, runId, patch);
|
|
189
|
+
if (!next) {
|
|
190
|
+
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
191
|
+
}
|
|
192
|
+
return next;
|
|
193
|
+
}
|
|
194
|
+
function setRunStatusOrFail(projectRoot, runId, to, options = {}) {
|
|
195
|
+
const next = setAuthorityRunStatus(projectRoot, runId, to, {
|
|
196
|
+
...options,
|
|
197
|
+
actor: { kind: "agent" }
|
|
198
|
+
});
|
|
199
|
+
if (!next) {
|
|
200
|
+
throw new CliError(`Run not found: ${runId}`, 2, { hint: "Run `rig run list` to see recorded runs." });
|
|
180
201
|
}
|
|
181
|
-
const next = {
|
|
182
|
-
...current,
|
|
183
|
-
...patch,
|
|
184
|
-
updatedAt: new Date().toISOString()
|
|
185
|
-
};
|
|
186
|
-
writeJsonFile2(resolve3(resolveAuthorityRunDir2(projectRoot, runId), "run.json"), next);
|
|
187
202
|
return next;
|
|
188
203
|
}
|
|
189
204
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -191,21 +206,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
191
206
|
if (!current) {
|
|
192
207
|
return;
|
|
193
208
|
}
|
|
194
|
-
|
|
209
|
+
writeJsonFile(resolve3(resolveAuthorityRunDir(projectRoot, runId), "run.json"), {
|
|
195
210
|
...current,
|
|
196
211
|
updatedAt: new Date().toISOString()
|
|
197
212
|
});
|
|
198
213
|
}
|
|
199
214
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
200
|
-
|
|
215
|
+
appendRunTimelineEntry(projectRoot, runId, value);
|
|
201
216
|
touchAuthorityRun(projectRoot, runId);
|
|
202
217
|
}
|
|
203
218
|
function appendRunLog(projectRoot, runId, value) {
|
|
204
|
-
|
|
219
|
+
appendRunLogEntry(projectRoot, runId, value);
|
|
205
220
|
touchAuthorityRun(projectRoot, runId);
|
|
206
221
|
}
|
|
207
222
|
function appendRunAction(projectRoot, runId, value) {
|
|
208
|
-
|
|
223
|
+
appendRunTimelineEntry(projectRoot, runId, {
|
|
209
224
|
id: value.id,
|
|
210
225
|
type: "action",
|
|
211
226
|
actionType: value.actionType,
|
|
@@ -259,8 +274,43 @@ function startRunAction(projectRoot, runId, input) {
|
|
|
259
274
|
}
|
|
260
275
|
};
|
|
261
276
|
}
|
|
277
|
+
var runEventSinkProjectRoot = null;
|
|
278
|
+
function configureRunEventSink(projectRoot) {
|
|
279
|
+
runEventSinkProjectRoot = projectRoot;
|
|
280
|
+
}
|
|
281
|
+
var lastDoorbellRangAt = 0;
|
|
282
|
+
function ringServerRunDoorbell(runId) {
|
|
283
|
+
const serverUrl = process.env.RIG_SERVER_URL?.trim();
|
|
284
|
+
if (!serverUrl)
|
|
285
|
+
return;
|
|
286
|
+
const now = Date.now();
|
|
287
|
+
if (now - lastDoorbellRangAt < 250)
|
|
288
|
+
return;
|
|
289
|
+
lastDoorbellRangAt = now;
|
|
290
|
+
const token = process.env.RIG_AUTH_TOKEN || process.env.RIG_GITHUB_TOKEN || "";
|
|
291
|
+
fetch(`${serverUrl.replace(/\/+$/, "")}/api/runs/doorbell`, {
|
|
292
|
+
method: "POST",
|
|
293
|
+
headers: {
|
|
294
|
+
"content-type": "application/json",
|
|
295
|
+
authorization: `Bearer ${token}`
|
|
296
|
+
},
|
|
297
|
+
body: JSON.stringify({ runId }),
|
|
298
|
+
signal: AbortSignal.timeout(1000)
|
|
299
|
+
}).catch(() => {});
|
|
300
|
+
}
|
|
262
301
|
function emitServerRunEvent(event) {
|
|
263
302
|
console.log(`__RIG_RUN_EVENT__${JSON.stringify({ ...event, at: new Date().toISOString() })}`);
|
|
303
|
+
if (runEventSinkProjectRoot) {
|
|
304
|
+
try {
|
|
305
|
+
if (event.type === "status" || event.type === "completed" || event.type === "failed") {
|
|
306
|
+
appendRunJournalEvent(runEventSinkProjectRoot, event.runId, {
|
|
307
|
+
type: "timeline-entry",
|
|
308
|
+
payload: { kind: "driver-event", ...event }
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
} catch {}
|
|
312
|
+
ringServerRunDoorbell(event.runId);
|
|
313
|
+
}
|
|
264
314
|
}
|
|
265
315
|
function buildRunPrompt(input) {
|
|
266
316
|
const initialPrompt = input.initialPrompt?.trim() || null;
|
|
@@ -317,6 +367,7 @@ ${acceptance}` : null,
|
|
|
317
367
|
${sourceContractLines.join(`
|
|
318
368
|
`)}` : null,
|
|
319
369
|
scopeText || renderSourceScopeValidation(sourceTask, sourceValidation) || null,
|
|
370
|
+
readPriorPrProgressPromptSection(input.projectRoot, input.taskId),
|
|
320
371
|
initialPrompt ? `Additional operator guidance:
|
|
321
372
|
${initialPrompt}` : null,
|
|
322
373
|
providerLines.length > 0 ? `Provider-specific runtime tooling:
|
|
@@ -388,6 +439,324 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
388
439
|
`);
|
|
389
440
|
}
|
|
390
441
|
|
|
442
|
+
// packages/cli/src/commands/_server-client.ts
|
|
443
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
444
|
+
import { resolve as resolve5 } from "path";
|
|
445
|
+
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
446
|
+
|
|
447
|
+
// packages/cli/src/commands/_connection-state.ts
|
|
448
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
449
|
+
import { homedir } from "os";
|
|
450
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
451
|
+
function resolveGlobalConnectionsPath(env = process.env) {
|
|
452
|
+
const explicit = env.RIG_CONNECTIONS_FILE?.trim();
|
|
453
|
+
if (explicit)
|
|
454
|
+
return resolve4(explicit);
|
|
455
|
+
const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
|
|
456
|
+
if (stateDir)
|
|
457
|
+
return resolve4(stateDir, "connections.json");
|
|
458
|
+
return resolve4(homedir(), ".rig", "connections.json");
|
|
459
|
+
}
|
|
460
|
+
function resolveRepoConnectionPath(projectRoot) {
|
|
461
|
+
return resolve4(projectRoot, ".rig", "state", "connection.json");
|
|
462
|
+
}
|
|
463
|
+
function readJsonFile(path) {
|
|
464
|
+
if (!existsSync2(path))
|
|
465
|
+
return null;
|
|
466
|
+
try {
|
|
467
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
468
|
+
} catch (error) {
|
|
469
|
+
throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function writeJsonFile2(path, value) {
|
|
473
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
474
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
475
|
+
`, "utf8");
|
|
476
|
+
}
|
|
477
|
+
function normalizeConnection(value) {
|
|
478
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
479
|
+
return null;
|
|
480
|
+
const record = value;
|
|
481
|
+
if (record.kind === "local")
|
|
482
|
+
return { kind: "local", mode: "auto" };
|
|
483
|
+
if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
|
|
484
|
+
const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
|
|
485
|
+
return { kind: "remote", baseUrl };
|
|
486
|
+
}
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
function readGlobalConnections(options = {}) {
|
|
490
|
+
const path = resolveGlobalConnectionsPath(options.env ?? process.env);
|
|
491
|
+
const payload = readJsonFile(path);
|
|
492
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
493
|
+
return { connections: {} };
|
|
494
|
+
}
|
|
495
|
+
const rawConnections = payload.connections;
|
|
496
|
+
const connections = {};
|
|
497
|
+
if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
|
|
498
|
+
for (const [alias, raw] of Object.entries(rawConnections)) {
|
|
499
|
+
const connection = normalizeConnection(raw);
|
|
500
|
+
if (connection)
|
|
501
|
+
connections[alias] = connection;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return { connections };
|
|
505
|
+
}
|
|
506
|
+
function readRepoConnection(projectRoot) {
|
|
507
|
+
const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
|
|
508
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
509
|
+
return null;
|
|
510
|
+
const record = payload;
|
|
511
|
+
const selected = typeof record.selected === "string" ? record.selected.trim() : "";
|
|
512
|
+
if (!selected)
|
|
513
|
+
return null;
|
|
514
|
+
return {
|
|
515
|
+
selected,
|
|
516
|
+
project: typeof record.project === "string" ? record.project : undefined,
|
|
517
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
518
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function writeRepoConnection(projectRoot, state) {
|
|
522
|
+
writeJsonFile2(resolveRepoConnectionPath(projectRoot), state);
|
|
523
|
+
}
|
|
524
|
+
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
525
|
+
const repo = readRepoConnection(projectRoot);
|
|
526
|
+
if (!repo)
|
|
527
|
+
return null;
|
|
528
|
+
if (repo.selected === "local")
|
|
529
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
530
|
+
const global = readGlobalConnections(options);
|
|
531
|
+
const connection = global.connections[repo.selected];
|
|
532
|
+
if (!connection) {
|
|
533
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
534
|
+
}
|
|
535
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
536
|
+
}
|
|
537
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
538
|
+
const repo = readRepoConnection(projectRoot);
|
|
539
|
+
if (!repo)
|
|
540
|
+
return;
|
|
541
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// packages/cli/src/commands/_server-client.ts
|
|
545
|
+
var scopedGitHubBearerTokens = new Map;
|
|
546
|
+
var serverPhaseListener = null;
|
|
547
|
+
function reportServerPhase(label) {
|
|
548
|
+
serverPhaseListener?.(label);
|
|
549
|
+
}
|
|
550
|
+
function cleanToken(value) {
|
|
551
|
+
const trimmed = value?.trim();
|
|
552
|
+
return trimmed ? trimmed : null;
|
|
553
|
+
}
|
|
554
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
555
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
556
|
+
if (!existsSync3(path))
|
|
557
|
+
return null;
|
|
558
|
+
try {
|
|
559
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
560
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
561
|
+
} catch {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
566
|
+
const scopedKey = resolve5(projectRoot);
|
|
567
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
568
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
569
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
570
|
+
if (privateSession)
|
|
571
|
+
return privateSession;
|
|
572
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
573
|
+
}
|
|
574
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
575
|
+
const path = resolve5(projectRoot, ".rig", "state", "github-auth.json");
|
|
576
|
+
if (!existsSync3(path))
|
|
577
|
+
return null;
|
|
578
|
+
try {
|
|
579
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
580
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
581
|
+
} catch {
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
586
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
587
|
+
}
|
|
588
|
+
async function ensureServerForCli(projectRoot) {
|
|
589
|
+
try {
|
|
590
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
591
|
+
if (selected?.connection.kind === "remote") {
|
|
592
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
593
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
594
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
595
|
+
return {
|
|
596
|
+
baseUrl: selected.connection.baseUrl,
|
|
597
|
+
authToken,
|
|
598
|
+
connectionKind: "remote",
|
|
599
|
+
serverProjectRoot
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
603
|
+
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
604
|
+
return {
|
|
605
|
+
baseUrl: connection.baseUrl,
|
|
606
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
607
|
+
connectionKind: "local",
|
|
608
|
+
serverProjectRoot: resolve5(projectRoot)
|
|
609
|
+
};
|
|
610
|
+
} catch (error) {
|
|
611
|
+
if (error instanceof Error) {
|
|
612
|
+
throw new CliError(error.message, 1);
|
|
613
|
+
}
|
|
614
|
+
throw error;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
618
|
+
const repo = readRepoConnection(projectRoot);
|
|
619
|
+
const slug = repo?.project?.trim();
|
|
620
|
+
if (!slug)
|
|
621
|
+
return null;
|
|
622
|
+
try {
|
|
623
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
624
|
+
headers: mergeHeaders(undefined, authToken)
|
|
625
|
+
});
|
|
626
|
+
if (!response.ok)
|
|
627
|
+
return null;
|
|
628
|
+
const payload = await response.json();
|
|
629
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
630
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
631
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
632
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
633
|
+
if (path)
|
|
634
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
635
|
+
return path;
|
|
636
|
+
} catch {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function mergeHeaders(headers, authToken) {
|
|
641
|
+
const merged = new Headers(headers);
|
|
642
|
+
if (authToken) {
|
|
643
|
+
merged.set("authorization", `Bearer ${authToken}`);
|
|
644
|
+
}
|
|
645
|
+
return merged;
|
|
646
|
+
}
|
|
647
|
+
function diagnosticMessage(payload) {
|
|
648
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
649
|
+
return null;
|
|
650
|
+
const record = payload;
|
|
651
|
+
const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
|
|
652
|
+
const messages = diagnostics.flatMap((entry) => {
|
|
653
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
654
|
+
return [];
|
|
655
|
+
const diagnostic = entry;
|
|
656
|
+
const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
|
|
657
|
+
const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
|
|
658
|
+
return message ? [`${kind}: ${message}`] : [];
|
|
659
|
+
});
|
|
660
|
+
return messages.length > 0 ? messages.join("; ") : null;
|
|
661
|
+
}
|
|
662
|
+
var serverReachabilityCache = new Map;
|
|
663
|
+
async function probeServerReachability(baseUrl, authToken) {
|
|
664
|
+
try {
|
|
665
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
|
|
666
|
+
headers: mergeHeaders(undefined, authToken),
|
|
667
|
+
signal: AbortSignal.timeout(1500)
|
|
668
|
+
});
|
|
669
|
+
return response.ok;
|
|
670
|
+
} catch {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function cachedServerReachability(projectRoot, baseUrl, authToken) {
|
|
675
|
+
const key = resolve5(projectRoot);
|
|
676
|
+
const cached = serverReachabilityCache.get(key);
|
|
677
|
+
if (cached)
|
|
678
|
+
return cached;
|
|
679
|
+
const probe = probeServerReachability(baseUrl, authToken);
|
|
680
|
+
serverReachabilityCache.set(key, probe);
|
|
681
|
+
return probe;
|
|
682
|
+
}
|
|
683
|
+
function describeSelectedServer(projectRoot, server) {
|
|
684
|
+
try {
|
|
685
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
686
|
+
if (selected) {
|
|
687
|
+
return {
|
|
688
|
+
alias: selected.alias,
|
|
689
|
+
target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
} catch {}
|
|
693
|
+
return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
|
|
694
|
+
}
|
|
695
|
+
async function buildServerFailureContext(projectRoot, server) {
|
|
696
|
+
const { alias, target } = describeSelectedServer(projectRoot, server);
|
|
697
|
+
const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
|
|
698
|
+
const reachability = reachable ? "server is reachable" : "server is unreachable";
|
|
699
|
+
return {
|
|
700
|
+
contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
|
|
701
|
+
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
async function requestServerJson(context, pathname, init = {}) {
|
|
705
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
706
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
707
|
+
if (server.serverProjectRoot)
|
|
708
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
709
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
710
|
+
let response;
|
|
711
|
+
try {
|
|
712
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
713
|
+
...init,
|
|
714
|
+
headers
|
|
715
|
+
});
|
|
716
|
+
} catch (error) {
|
|
717
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
718
|
+
throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
|
|
719
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
720
|
+
}
|
|
721
|
+
const text = await response.text();
|
|
722
|
+
const payload = text.trim().length > 0 ? (() => {
|
|
723
|
+
try {
|
|
724
|
+
return JSON.parse(text);
|
|
725
|
+
} catch {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
})() : null;
|
|
729
|
+
if (!response.ok) {
|
|
730
|
+
const diagnostics = diagnosticMessage(payload);
|
|
731
|
+
const rawDetail = diagnostics ?? (text || response.statusText);
|
|
732
|
+
const detail = diagnostics ? rawDetail : rawDetail.split(`
|
|
733
|
+
`).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
|
|
734
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
735
|
+
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
736
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
737
|
+
}
|
|
738
|
+
return payload;
|
|
739
|
+
}
|
|
740
|
+
async function updateWorkspaceTaskViaServer(context, input) {
|
|
741
|
+
const payload = await requestServerJson(context, "/api/tasks/update", {
|
|
742
|
+
method: "POST",
|
|
743
|
+
headers: { "content-type": "application/json" },
|
|
744
|
+
body: JSON.stringify(input)
|
|
745
|
+
});
|
|
746
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
747
|
+
}
|
|
748
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
749
|
+
"created",
|
|
750
|
+
"preparing",
|
|
751
|
+
"running",
|
|
752
|
+
"validating",
|
|
753
|
+
"reviewing",
|
|
754
|
+
"stopped",
|
|
755
|
+
"failed",
|
|
756
|
+
"needs-attention",
|
|
757
|
+
"needs_attention"
|
|
758
|
+
]);
|
|
759
|
+
|
|
391
760
|
// packages/cli/src/commands/task-run-driver.ts
|
|
392
761
|
var PI_CANONICAL_RUN_STAGES = [
|
|
393
762
|
"Connect",
|
|
@@ -412,6 +781,19 @@ function looksLikeGitHubToken(value) {
|
|
|
412
781
|
return false;
|
|
413
782
|
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
414
783
|
}
|
|
784
|
+
function isSensitivePayloadKey(key) {
|
|
785
|
+
return /token|secret|password|authorization|credential/i.test(key);
|
|
786
|
+
}
|
|
787
|
+
function redactSensitivePayload(value) {
|
|
788
|
+
if (Array.isArray(value))
|
|
789
|
+
return value.map(redactSensitivePayload);
|
|
790
|
+
if (!value || typeof value !== "object")
|
|
791
|
+
return value;
|
|
792
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
793
|
+
key,
|
|
794
|
+
isSensitivePayloadKey(key) && typeof child === "string" && child.trim() ? "[redacted]" : redactSensitivePayload(child)
|
|
795
|
+
]));
|
|
796
|
+
}
|
|
415
797
|
function githubBridgeEnv(token) {
|
|
416
798
|
const clean = token?.trim();
|
|
417
799
|
if (!clean)
|
|
@@ -431,6 +813,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
431
813
|
RIG_SERVER_RUN_ID: input.runId,
|
|
432
814
|
RIG_TASK_ID: input.taskId,
|
|
433
815
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
816
|
+
RIG_STEERING_POLL_MS: "0",
|
|
434
817
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
435
818
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
436
819
|
...githubBridgeEnv(githubToken)
|
|
@@ -455,12 +838,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
455
838
|
return 0;
|
|
456
839
|
let copied = 0;
|
|
457
840
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
458
|
-
const sourcePath =
|
|
459
|
-
const targetPath =
|
|
841
|
+
const sourcePath = resolve6(sourceRoot, relativePath);
|
|
842
|
+
const targetPath = resolve6(targetRoot, relativePath);
|
|
460
843
|
try {
|
|
461
844
|
if (!statSync(sourcePath).isFile())
|
|
462
845
|
continue;
|
|
463
|
-
|
|
846
|
+
mkdirSync2(resolve6(targetPath, ".."), { recursive: true });
|
|
464
847
|
copyFileSync(sourcePath, targetPath);
|
|
465
848
|
copied += 1;
|
|
466
849
|
} catch {}
|
|
@@ -499,7 +882,7 @@ function buildDirtyBaselineHandshakeEnv(input) {
|
|
|
499
882
|
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
500
883
|
return {
|
|
501
884
|
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
502
|
-
RIG_DIRTY_BASELINE_READY_FILE:
|
|
885
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve6(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
503
886
|
};
|
|
504
887
|
}
|
|
505
888
|
function positiveInt(value, fallback) {
|
|
@@ -507,7 +890,7 @@ function positiveInt(value, fallback) {
|
|
|
507
890
|
}
|
|
508
891
|
function resolveTaskRunAutomationLimits(config, env = process.env) {
|
|
509
892
|
const configuredValidationAttempts = positiveInt(config?.automation?.maxValidationAttempts, 30);
|
|
510
|
-
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations,
|
|
893
|
+
const configuredPrFixIterations = positiveInt(config?.automation?.maxPrFixIterations, 100500);
|
|
511
894
|
return {
|
|
512
895
|
maxValidationAttempts: parsePositiveInt(env.RIG_TASK_RUN_MAX_ATTEMPTS, "RIG_TASK_RUN_MAX_ATTEMPTS", configuredValidationAttempts),
|
|
513
896
|
maxPrFixIterations: configuredPrFixIterations
|
|
@@ -601,12 +984,6 @@ function appendPiStageLog(input) {
|
|
|
601
984
|
});
|
|
602
985
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
603
986
|
}
|
|
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
987
|
function createCommandRunner(binary) {
|
|
611
988
|
return async (args, options) => {
|
|
612
989
|
const child = spawn(binary, [...args], {
|
|
@@ -617,9 +994,9 @@ function createCommandRunner(binary) {
|
|
|
617
994
|
const stderrChunks = [];
|
|
618
995
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
619
996
|
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) =>
|
|
997
|
+
return await new Promise((resolve7) => {
|
|
998
|
+
child.once("error", (error) => resolve7({ exitCode: 1, stderr: error.message }));
|
|
999
|
+
child.once("close", (code) => resolve7({
|
|
623
1000
|
exitCode: code ?? 1,
|
|
624
1001
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
625
1002
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -643,8 +1020,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
643
1020
|
if (!taskId) {
|
|
644
1021
|
return { status: "skipped" };
|
|
645
1022
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
1023
|
+
const configInput = input.config ?? null;
|
|
1024
|
+
const config = configInput ?? {};
|
|
1025
|
+
const prMode = configInput ? configInput.pr?.mode ?? "auto" : "off";
|
|
648
1026
|
if (prMode === "off" || prMode === "ask") {
|
|
649
1027
|
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
1028
|
input.appendStage?.("Complete", "Validation completed; no PR was opened and the issue was left open.", "completed", "info");
|
|
@@ -660,7 +1038,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
660
1038
|
gitCommand
|
|
661
1039
|
});
|
|
662
1040
|
const prAutomation = input.prAutomation ?? runPrAutomation;
|
|
663
|
-
const updateTaskSource = input.updateTaskSource ??
|
|
1041
|
+
const updateTaskSource = input.updateTaskSource ?? updateTaskSourceWithProjectSync;
|
|
664
1042
|
const stage = input.appendStage ?? (() => {
|
|
665
1043
|
return;
|
|
666
1044
|
});
|
|
@@ -682,7 +1060,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
682
1060
|
command: gitCommand
|
|
683
1061
|
});
|
|
684
1062
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
685
|
-
await
|
|
1063
|
+
await pushBranchSyncedWithOrigin({ projectRoot: workspace, branch, gitCommand });
|
|
686
1064
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
687
1065
|
const pr = await prAutomation({
|
|
688
1066
|
projectRoot: workspace,
|
|
@@ -692,7 +1070,9 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
692
1070
|
config,
|
|
693
1071
|
sourceTask: input.sourceTask,
|
|
694
1072
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
1073
|
+
artifactRoot: resolve6(input.projectRoot, "artifacts", taskId),
|
|
695
1074
|
command: ghCommand,
|
|
1075
|
+
gitCommand,
|
|
696
1076
|
steerPi,
|
|
697
1077
|
lifecycle: {
|
|
698
1078
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -741,8 +1121,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
741
1121
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
742
1122
|
})
|
|
743
1123
|
}
|
|
744
|
-
}).catch(() => {
|
|
745
|
-
return;
|
|
746
1124
|
});
|
|
747
1125
|
}
|
|
748
1126
|
},
|
|
@@ -761,8 +1139,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
761
1139
|
runtimeWorkspace: input.runtimeWorkspace ?? null
|
|
762
1140
|
})
|
|
763
1141
|
}
|
|
764
|
-
}).catch(() => {
|
|
765
|
-
return;
|
|
766
1142
|
});
|
|
767
1143
|
}
|
|
768
1144
|
},
|
|
@@ -791,8 +1167,17 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
791
1167
|
errorText: detail
|
|
792
1168
|
})
|
|
793
1169
|
}
|
|
794
|
-
}).catch(() => {
|
|
795
|
-
|
|
1170
|
+
}).catch((error) => {
|
|
1171
|
+
try {
|
|
1172
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1173
|
+
id: `log:${input.runId}:task-source-needs-attention-update`,
|
|
1174
|
+
title: "Task source needs-attention update failed",
|
|
1175
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
1176
|
+
tone: "error",
|
|
1177
|
+
status: "needs_attention",
|
|
1178
|
+
createdAt: new Date().toISOString()
|
|
1179
|
+
});
|
|
1180
|
+
} catch {}
|
|
796
1181
|
});
|
|
797
1182
|
}
|
|
798
1183
|
return { status: "needs_attention", pr };
|
|
@@ -815,7 +1200,7 @@ function summarizeValidationFailure(projectRoot, taskId) {
|
|
|
815
1200
|
return null;
|
|
816
1201
|
}
|
|
817
1202
|
for (const artifactDir of resolveTaskArtifactDirs(projectRoot, taskId)) {
|
|
818
|
-
const summary =
|
|
1203
|
+
const summary = readJsonFile2(resolve6(artifactDir, "validation-summary.json"), null);
|
|
819
1204
|
if (!summary || summary.status !== "fail") {
|
|
820
1205
|
continue;
|
|
821
1206
|
}
|
|
@@ -888,7 +1273,7 @@ function parsePositiveInt(value, option, fallback) {
|
|
|
888
1273
|
}
|
|
889
1274
|
const parsed = Number.parseInt(value, 10);
|
|
890
1275
|
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
891
|
-
throw new
|
|
1276
|
+
throw new CliError(`Invalid ${option} value: ${value}`, 1, { hint: `Pass a positive integer, e.g. \`${option} 10\`.` });
|
|
892
1277
|
}
|
|
893
1278
|
return parsed;
|
|
894
1279
|
}
|
|
@@ -896,14 +1281,14 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
896
1281
|
if (!input.taskId || !input.workspaceDir) {
|
|
897
1282
|
return { accepted: false, reason: null };
|
|
898
1283
|
}
|
|
899
|
-
const artifactDir =
|
|
900
|
-
const reviewStatusPath =
|
|
901
|
-
const taskResultPath =
|
|
1284
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1285
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1286
|
+
const taskResultPath = resolve6(artifactDir, "task-result.json");
|
|
902
1287
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
903
1288
|
if (reviewStatus !== "APPROVED") {
|
|
904
1289
|
return { accepted: false, reason: null };
|
|
905
1290
|
}
|
|
906
|
-
const taskResult =
|
|
1291
|
+
const taskResult = readJsonFile2(taskResultPath, null);
|
|
907
1292
|
if (taskResult?.status !== "completed") {
|
|
908
1293
|
return { accepted: false, reason: null };
|
|
909
1294
|
}
|
|
@@ -935,13 +1320,13 @@ function resolveTaskRunRetryContext(input) {
|
|
|
935
1320
|
if (!input.taskId || !input.workspaceDir) {
|
|
936
1321
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
937
1322
|
}
|
|
938
|
-
const artifactDir =
|
|
939
|
-
const reviewStatePath =
|
|
940
|
-
const reviewFeedbackPath =
|
|
941
|
-
const reviewStatusPath =
|
|
942
|
-
const failedApproachesPath =
|
|
943
|
-
const validationSummaryPath =
|
|
944
|
-
const reviewState =
|
|
1323
|
+
const artifactDir = resolve6(input.workspaceDir, "artifacts", input.taskId);
|
|
1324
|
+
const reviewStatePath = resolve6(artifactDir, "review-state.json");
|
|
1325
|
+
const reviewFeedbackPath = resolve6(artifactDir, "review-feedback.md");
|
|
1326
|
+
const reviewStatusPath = resolve6(artifactDir, "review-status.txt");
|
|
1327
|
+
const failedApproachesPath = resolve6(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
1328
|
+
const validationSummaryPath = resolve6(artifactDir, "validation-summary.json");
|
|
1329
|
+
const reviewState = readJsonFile2(reviewStatePath, null);
|
|
945
1330
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
946
1331
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
947
1332
|
if (reviewStatus === "APPROVED") {
|
|
@@ -994,12 +1379,143 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
994
1379
|
}
|
|
995
1380
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
996
1381
|
}
|
|
1382
|
+
function appendAssistantTimelineFromRecord(input) {
|
|
1383
|
+
let nextAssistantText = input.assistantText;
|
|
1384
|
+
if (input.record.type === "message_update") {
|
|
1385
|
+
const assistantMessageEvent = input.record.assistantMessageEvent && typeof input.record.assistantMessageEvent === "object" ? input.record.assistantMessageEvent : null;
|
|
1386
|
+
if (assistantMessageEvent?.type === "text_delta" && typeof assistantMessageEvent.delta === "string") {
|
|
1387
|
+
nextAssistantText += assistantMessageEvent.delta;
|
|
1388
|
+
}
|
|
1389
|
+
} else if (input.record.type === "stream_event") {
|
|
1390
|
+
const event = input.record.event && typeof input.record.event === "object" ? input.record.event : null;
|
|
1391
|
+
const delta = event?.delta && typeof event.delta === "object" ? event.delta : null;
|
|
1392
|
+
if (delta?.type === "text_delta" && typeof delta.text === "string") {
|
|
1393
|
+
nextAssistantText += delta.text;
|
|
1394
|
+
}
|
|
1395
|
+
} else if (input.record.type === "assistant") {
|
|
1396
|
+
const message = input.record.message && typeof input.record.message === "object" ? input.record.message : input.record;
|
|
1397
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
1398
|
+
const fullText = content.map((entry) => entry && typeof entry === "object" && entry.type === "text" ? String(entry.text ?? "") : "").join("");
|
|
1399
|
+
if (fullText.length > nextAssistantText.length)
|
|
1400
|
+
nextAssistantText = fullText;
|
|
1401
|
+
}
|
|
1402
|
+
if (nextAssistantText !== input.assistantText) {
|
|
1403
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1404
|
+
id: input.messageId,
|
|
1405
|
+
type: "assistant_message",
|
|
1406
|
+
text: nextAssistantText,
|
|
1407
|
+
state: "streaming",
|
|
1408
|
+
createdAt: new Date().toISOString()
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
return nextAssistantText;
|
|
1412
|
+
}
|
|
1413
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
1414
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1415
|
+
if (type === "response") {
|
|
1416
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
1417
|
+
const success = input.record.success !== false;
|
|
1418
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
1419
|
+
return true;
|
|
1420
|
+
}
|
|
1421
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1422
|
+
id: input.nextRunLogId(),
|
|
1423
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
1424
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
1425
|
+
tone: success ? "tool" : "error",
|
|
1426
|
+
status: input.status,
|
|
1427
|
+
payload: input.record,
|
|
1428
|
+
createdAt: new Date().toISOString()
|
|
1429
|
+
});
|
|
1430
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
1431
|
+
return true;
|
|
1432
|
+
}
|
|
1433
|
+
if (type !== "extension_ui_request")
|
|
1434
|
+
return false;
|
|
1435
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
1436
|
+
let title = "Pi UI event";
|
|
1437
|
+
let detail = method;
|
|
1438
|
+
let tone = "info";
|
|
1439
|
+
if (method === "notify") {
|
|
1440
|
+
title = "Pi notification";
|
|
1441
|
+
detail = String(input.record.message ?? "");
|
|
1442
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
1443
|
+
} else if (method === "setStatus") {
|
|
1444
|
+
title = "Pi UI status";
|
|
1445
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
1446
|
+
tone = "tool";
|
|
1447
|
+
} else if (method === "setWidget") {
|
|
1448
|
+
title = "Pi UI widget";
|
|
1449
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
1450
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
1451
|
+
tone = "tool";
|
|
1452
|
+
} else if (method === "setTitle") {
|
|
1453
|
+
title = "Pi UI title";
|
|
1454
|
+
detail = String(input.record.title ?? "");
|
|
1455
|
+
tone = "tool";
|
|
1456
|
+
} else if (method === "set_editor_text") {
|
|
1457
|
+
title = "Pi editor update";
|
|
1458
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
1459
|
+
tone = "tool";
|
|
1460
|
+
} else {
|
|
1461
|
+
title = "Pi UI request";
|
|
1462
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
1463
|
+
}
|
|
1464
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
1465
|
+
id: input.nextRunLogId(),
|
|
1466
|
+
title,
|
|
1467
|
+
detail,
|
|
1468
|
+
tone,
|
|
1469
|
+
status: input.status,
|
|
1470
|
+
payload: input.record,
|
|
1471
|
+
createdAt: new Date().toISOString()
|
|
1472
|
+
});
|
|
1473
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
1474
|
+
return true;
|
|
1475
|
+
}
|
|
1476
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
1477
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
1478
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
1479
|
+
return false;
|
|
1480
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
1481
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
1482
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
1483
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1484
|
+
id: `tool:${toolCallId}:${type}`,
|
|
1485
|
+
type,
|
|
1486
|
+
toolName,
|
|
1487
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
1488
|
+
createdAt: new Date().toISOString()
|
|
1489
|
+
});
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
1493
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
1494
|
+
return type === "pi.event" || type === "status.update" || type === "activity.update" || type === "ready" || 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");
|
|
1495
|
+
}
|
|
1496
|
+
function appendToolTimelineFromLog(input) {
|
|
1497
|
+
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
1498
|
+
if (title !== "Tool activity")
|
|
1499
|
+
return;
|
|
1500
|
+
const payload = input.log.payload && typeof input.log.payload === "object" && !Array.isArray(input.log.payload) ? input.log.payload : {};
|
|
1501
|
+
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;
|
|
1502
|
+
const logId = typeof input.log.id === "string" && input.log.id.trim() ? input.log.id.trim() : `${Date.now()}`;
|
|
1503
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
1504
|
+
id: `tool:${logId}`,
|
|
1505
|
+
type: "tool_execution_update",
|
|
1506
|
+
toolName,
|
|
1507
|
+
status: typeof input.log.status === "string" ? input.log.status : "running",
|
|
1508
|
+
detail: typeof input.log.detail === "string" ? input.log.detail : null,
|
|
1509
|
+
payload,
|
|
1510
|
+
createdAt: typeof input.log.createdAt === "string" ? input.log.createdAt : new Date().toISOString()
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
997
1513
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
998
|
-
if (!
|
|
1514
|
+
if (!existsSync4(reviewStatusPath)) {
|
|
999
1515
|
return null;
|
|
1000
1516
|
}
|
|
1001
1517
|
try {
|
|
1002
|
-
const status =
|
|
1518
|
+
const status = readFileSync4(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
1003
1519
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
1004
1520
|
} catch {
|
|
1005
1521
|
return null;
|
|
@@ -1017,7 +1533,38 @@ function isTaskRunReviewRejected(reviewState) {
|
|
|
1017
1533
|
function runSourceTaskIdentity(sourceTask) {
|
|
1018
1534
|
return sourceTask;
|
|
1019
1535
|
}
|
|
1020
|
-
|
|
1536
|
+
function sourceTaskIssueNodeId(sourceTask) {
|
|
1537
|
+
if (!sourceTask || typeof sourceTask !== "object" || Array.isArray(sourceTask))
|
|
1538
|
+
return null;
|
|
1539
|
+
const record = sourceTask;
|
|
1540
|
+
const direct = typeof record.issueNodeId === "string" ? record.issueNodeId : typeof record.nodeId === "string" ? record.nodeId : typeof record.node_id === "string" ? record.node_id : null;
|
|
1541
|
+
if (direct?.trim())
|
|
1542
|
+
return direct.trim();
|
|
1543
|
+
const raw = record.raw && typeof record.raw === "object" && !Array.isArray(record.raw) ? record.raw : null;
|
|
1544
|
+
return typeof raw?.id === "string" && raw.id.trim() ? raw.id.trim() : null;
|
|
1545
|
+
}
|
|
1546
|
+
var updateTaskSourceWithProjectSync = async (projectRoot, input) => {
|
|
1547
|
+
const serverResult = await updateWorkspaceTaskViaServer({ projectRoot }, {
|
|
1548
|
+
id: input.taskId,
|
|
1549
|
+
...input.update.status ? { status: input.update.status } : {},
|
|
1550
|
+
...input.update.comment ? { comment: input.update.comment } : {},
|
|
1551
|
+
...input.update.title ? { title: input.update.title } : {},
|
|
1552
|
+
...typeof input.update.body === "string" ? { body: input.update.body } : {},
|
|
1553
|
+
issueNodeId: sourceTaskIssueNodeId(input.sourceTask)
|
|
1554
|
+
});
|
|
1555
|
+
if (serverResult.ok === false) {
|
|
1556
|
+
throw new Error(typeof serverResult.error === "string" ? serverResult.error : "Rig server task update failed.");
|
|
1557
|
+
}
|
|
1558
|
+
return {
|
|
1559
|
+
updated: serverResult.ok !== false,
|
|
1560
|
+
taskId: input.taskId,
|
|
1561
|
+
status: input.update.status,
|
|
1562
|
+
source: "server",
|
|
1563
|
+
sourceKind: "server",
|
|
1564
|
+
projectSync: serverResult.projectSync
|
|
1565
|
+
};
|
|
1566
|
+
};
|
|
1567
|
+
async function updateTaskSourceAfterDriverRun(projectRoot, runId, taskId, sourceTask, status, summary, input, updateTaskSource = updateTaskSourceWithProjectSync) {
|
|
1021
1568
|
if (!taskId)
|
|
1022
1569
|
return;
|
|
1023
1570
|
const config = await loadTaskRunAutomationConfig(projectRoot);
|
|
@@ -1081,9 +1628,14 @@ function stringArrayField(record, key) {
|
|
|
1081
1628
|
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
1082
1629
|
}
|
|
1083
1630
|
async function executeRigOwnedTaskRun(context, input) {
|
|
1631
|
+
process.stdout.on("error", () => {});
|
|
1632
|
+
process.stderr.on("error", () => {});
|
|
1633
|
+
configureRunEventSink(context.projectRoot);
|
|
1084
1634
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
1085
1635
|
const existingRunRecord = readAuthorityRun3(context.projectRoot, input.runId);
|
|
1086
1636
|
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
1637
|
+
let latestPiSessionId = typeof existingRunRecord?.piSession?.sessionId === "string" && existingRunRecord.piSession.sessionId.trim() ? existingRunRecord.piSession.sessionId : null;
|
|
1638
|
+
const sessionAnchor = () => latestPiSessionId ? { sessionId: latestPiSessionId } : {};
|
|
1087
1639
|
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
1088
1640
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
1089
1641
|
let prompt = buildRunPrompt({
|
|
@@ -1107,11 +1659,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1107
1659
|
...input.model ? ["--model", input.model] : [],
|
|
1108
1660
|
"--prompt"
|
|
1109
1661
|
] : input.runtimeAdapter === "pi" ? [
|
|
1110
|
-
"
|
|
1111
|
-
"--verbose",
|
|
1112
|
-
"--mode",
|
|
1113
|
-
"json",
|
|
1114
|
-
"--no-session",
|
|
1662
|
+
"__rig_pi_session_daemon__",
|
|
1115
1663
|
...input.model ? ["--model", input.model] : []
|
|
1116
1664
|
] : [
|
|
1117
1665
|
"--print",
|
|
@@ -1142,8 +1690,8 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1142
1690
|
runtimeAdapter: input.runtimeAdapter,
|
|
1143
1691
|
status: resumeMode ? "preparing" : "created"
|
|
1144
1692
|
});
|
|
1693
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "preparing", { reason: "driver-start" });
|
|
1145
1694
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1146
|
-
status: "preparing",
|
|
1147
1695
|
startedAt,
|
|
1148
1696
|
completedAt: null,
|
|
1149
1697
|
errorText: null,
|
|
@@ -1160,7 +1708,8 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1160
1708
|
type: "status",
|
|
1161
1709
|
runId: input.runId,
|
|
1162
1710
|
status: "preparing",
|
|
1163
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
1711
|
+
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
1712
|
+
...sessionAnchor()
|
|
1164
1713
|
});
|
|
1165
1714
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1166
1715
|
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
@@ -1186,15 +1735,15 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
1186
1735
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
1187
1736
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
1188
1737
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
1189
|
-
const planningArtifactPath =
|
|
1738
|
+
const planningArtifactPath = resolve6("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
1190
1739
|
const persistedPlanning = {
|
|
1191
1740
|
...planningClassification,
|
|
1192
1741
|
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
1193
1742
|
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
1194
1743
|
classifiedAt: new Date().toISOString()
|
|
1195
1744
|
};
|
|
1196
|
-
|
|
1197
|
-
|
|
1745
|
+
mkdirSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
1746
|
+
writeFileSync2(resolve6(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
1198
1747
|
`, "utf8");
|
|
1199
1748
|
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
1200
1749
|
prompt = `${prompt}
|
|
@@ -1208,7 +1757,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1208
1757
|
projectRoot: context.projectRoot,
|
|
1209
1758
|
runId: input.runId,
|
|
1210
1759
|
stage,
|
|
1211
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
1760
|
+
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
1761
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
1213
1762
|
});
|
|
1214
1763
|
}
|
|
@@ -1244,11 +1793,12 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1244
1793
|
let verificationStarted = false;
|
|
1245
1794
|
let reviewStarted = false;
|
|
1246
1795
|
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
1247
|
-
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ?
|
|
1796
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve6(existingRunRecord.sessionPath, "..") : null;
|
|
1248
1797
|
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
1249
1798
|
let latestProviderCommand = null;
|
|
1250
1799
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
1251
1800
|
let snapshotSidecarPromise = null;
|
|
1801
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
1252
1802
|
let dirtyBaselineApplied = false;
|
|
1253
1803
|
const childEnv = {
|
|
1254
1804
|
...process.env,
|
|
@@ -1279,8 +1829,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1279
1829
|
if (verificationStarted)
|
|
1280
1830
|
return;
|
|
1281
1831
|
verificationStarted = true;
|
|
1282
|
-
|
|
1283
|
-
emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null });
|
|
1832
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "validating");
|
|
1833
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "validating", detail: detail ?? null, ...sessionAnchor() });
|
|
1284
1834
|
verificationAction = startRunAction(context.projectRoot, input.runId, {
|
|
1285
1835
|
actionId: `action:${input.runId}:completion-verification`,
|
|
1286
1836
|
actionType: "completion-verification",
|
|
@@ -1292,8 +1842,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1292
1842
|
if (reviewStarted)
|
|
1293
1843
|
return;
|
|
1294
1844
|
reviewStarted = true;
|
|
1295
|
-
|
|
1296
|
-
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null });
|
|
1845
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "reviewing");
|
|
1846
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "reviewing", detail: detail ?? null, ...sessionAnchor() });
|
|
1297
1847
|
reviewAction = startRunAction(context.projectRoot, input.runId, {
|
|
1298
1848
|
actionId: `action:${input.runId}:review-closeout`,
|
|
1299
1849
|
actionType: "run.review",
|
|
@@ -1301,6 +1851,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1301
1851
|
detail: detail ?? "Verifier review is running."
|
|
1302
1852
|
});
|
|
1303
1853
|
};
|
|
1854
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1304
1855
|
const handleWrapperEvent = (rawPayload) => {
|
|
1305
1856
|
let event = null;
|
|
1306
1857
|
try {
|
|
@@ -1316,6 +1867,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1316
1867
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
1317
1868
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
1318
1869
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
1870
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
1319
1871
|
latestRuntimeBranch = runtimeId;
|
|
1320
1872
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
1321
1873
|
runtimeId: runtimeId ?? null,
|
|
@@ -1324,13 +1876,13 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1324
1876
|
logsDir: latestLogsDir,
|
|
1325
1877
|
contextFile: payload.contextFile ?? null
|
|
1326
1878
|
});
|
|
1879
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "running");
|
|
1327
1880
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1328
|
-
status: "running",
|
|
1329
1881
|
worktreePath: latestRuntimeWorkspace,
|
|
1330
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
1882
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve6(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
1331
1883
|
logRoot: latestLogsDir,
|
|
1332
|
-
sessionPath: latestSessionDir ?
|
|
1333
|
-
sessionLogPath: latestLogsDir ?
|
|
1884
|
+
sessionPath: latestSessionDir ? resolve6(latestSessionDir, "session.json") : null,
|
|
1885
|
+
sessionLogPath: latestLogsDir ? resolve6(latestLogsDir, "agent-stdout.log") : null,
|
|
1334
1886
|
branch: runtimeId
|
|
1335
1887
|
});
|
|
1336
1888
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
@@ -1338,8 +1890,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1338
1890
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
1339
1891
|
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
1340
1892
|
if (readyFile) {
|
|
1341
|
-
|
|
1342
|
-
|
|
1893
|
+
mkdirSync2(resolve6(readyFile, ".."), { recursive: true });
|
|
1894
|
+
writeFileSync2(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
1343
1895
|
`, "utf8");
|
|
1344
1896
|
}
|
|
1345
1897
|
appendRunLog(context.projectRoot, input.runId, {
|
|
@@ -1353,7 +1905,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1353
1905
|
});
|
|
1354
1906
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
1355
1907
|
}
|
|
1356
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1908
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
1357
1909
|
snapshotSidecarPromise = (async () => {
|
|
1358
1910
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
1359
1911
|
projectRoot: context.projectRoot,
|
|
@@ -1381,7 +1933,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1381
1933
|
type: "status",
|
|
1382
1934
|
runId: input.runId,
|
|
1383
1935
|
status: "running",
|
|
1384
|
-
detail: latestRuntimeWorkspace
|
|
1936
|
+
detail: latestRuntimeWorkspace,
|
|
1937
|
+
...sessionAnchor()
|
|
1385
1938
|
});
|
|
1386
1939
|
return true;
|
|
1387
1940
|
}
|
|
@@ -1435,9 +1988,79 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1435
1988
|
}
|
|
1436
1989
|
return true;
|
|
1437
1990
|
}
|
|
1438
|
-
|
|
1991
|
+
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") {
|
|
1992
|
+
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";
|
|
1993
|
+
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");
|
|
1994
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
1995
|
+
id: nextRunLogId(),
|
|
1996
|
+
title,
|
|
1997
|
+
detail,
|
|
1998
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
1999
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
2000
|
+
payload: {
|
|
2001
|
+
eventType: event.type,
|
|
2002
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
2003
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
2004
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
2005
|
+
},
|
|
2006
|
+
createdAt: new Date().toISOString()
|
|
2007
|
+
});
|
|
2008
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
2009
|
+
return true;
|
|
2010
|
+
}
|
|
2011
|
+
if (event.type === "pi.session.ready") {
|
|
2012
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
2013
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
2014
|
+
if (privateMetadata) {
|
|
2015
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2016
|
+
piSession: publicMetadata,
|
|
2017
|
+
piSessionPrivate: privateMetadata
|
|
2018
|
+
});
|
|
2019
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
2020
|
+
if (sessionId) {
|
|
2021
|
+
latestPiSessionId = sessionId;
|
|
2022
|
+
}
|
|
2023
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2024
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
2025
|
+
title: "Worker Pi session ready",
|
|
2026
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
2027
|
+
tone: "info",
|
|
2028
|
+
status: "running",
|
|
2029
|
+
payload: {
|
|
2030
|
+
sessionId: sessionId || null,
|
|
2031
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
2032
|
+
},
|
|
2033
|
+
createdAt: new Date().toISOString()
|
|
2034
|
+
});
|
|
2035
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
2036
|
+
}
|
|
2037
|
+
return true;
|
|
2038
|
+
}
|
|
2039
|
+
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") {
|
|
2040
|
+
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";
|
|
2041
|
+
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")}`;
|
|
2042
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2043
|
+
id: nextRunLogId(),
|
|
2044
|
+
title,
|
|
2045
|
+
detail,
|
|
2046
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
2047
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
2048
|
+
payload,
|
|
2049
|
+
createdAt: new Date().toISOString()
|
|
2050
|
+
});
|
|
2051
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
2052
|
+
return true;
|
|
2053
|
+
}
|
|
2054
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2055
|
+
id: `timeline:${input.runId}:${Date.now()}:wrapper:${event.type}`,
|
|
2056
|
+
type: "wrapper-event",
|
|
2057
|
+
eventType: event.type,
|
|
2058
|
+
payload: redactSensitivePayload(payload),
|
|
2059
|
+
createdAt: new Date().toISOString()
|
|
2060
|
+
});
|
|
2061
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2062
|
+
return true;
|
|
1439
2063
|
};
|
|
1440
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
1441
2064
|
const handleAgentStdoutLine = (line) => {
|
|
1442
2065
|
const trimmed = line.trim();
|
|
1443
2066
|
if (!trimmed)
|
|
@@ -1471,6 +2094,19 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1471
2094
|
try {
|
|
1472
2095
|
const record = JSON.parse(trimmed);
|
|
1473
2096
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
2097
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
2098
|
+
projectRoot: context.projectRoot,
|
|
2099
|
+
runId: input.runId,
|
|
2100
|
+
record,
|
|
2101
|
+
status: liveLogStatus,
|
|
2102
|
+
nextRunLogId
|
|
2103
|
+
})) {
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
2107
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2108
|
+
return;
|
|
2109
|
+
}
|
|
1474
2110
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
1475
2111
|
runId: input.runId,
|
|
1476
2112
|
record,
|
|
@@ -1487,7 +2123,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1487
2123
|
if (providerLogs.length > 0) {
|
|
1488
2124
|
for (const providerLog of providerLogs) {
|
|
1489
2125
|
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2126
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
1490
2127
|
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2128
|
+
if (providerLog.title === "Tool activity")
|
|
2129
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1491
2130
|
}
|
|
1492
2131
|
}
|
|
1493
2132
|
if (input.runtimeAdapter === "codex") {
|
|
@@ -1539,6 +2178,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1539
2178
|
return;
|
|
1540
2179
|
}
|
|
1541
2180
|
}
|
|
2181
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
1542
2184
|
if (record.type === "assistant") {
|
|
1543
2185
|
const message = record.message && typeof record.message === "object" ? record.message : record;
|
|
1544
2186
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -1645,7 +2287,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1645
2287
|
let acceptedArtifactObservedAt = null;
|
|
1646
2288
|
let acceptedArtifactPollTimer = null;
|
|
1647
2289
|
let acceptedArtifactKillTimer = null;
|
|
1648
|
-
const attemptExit = await new Promise((
|
|
2290
|
+
const attemptExit = await new Promise((resolve7) => {
|
|
1649
2291
|
let settled = false;
|
|
1650
2292
|
const settle = (result) => {
|
|
1651
2293
|
if (settled)
|
|
@@ -1653,7 +2295,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1653
2295
|
settled = true;
|
|
1654
2296
|
if (acceptedArtifactPollTimer)
|
|
1655
2297
|
clearInterval(acceptedArtifactPollTimer);
|
|
1656
|
-
|
|
2298
|
+
resolve7(result);
|
|
1657
2299
|
};
|
|
1658
2300
|
const pollAcceptedArtifacts = () => {
|
|
1659
2301
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -1726,11 +2368,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1726
2368
|
});
|
|
1727
2369
|
for (const pendingLog of pendingLogs) {
|
|
1728
2370
|
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2371
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
1729
2372
|
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2373
|
+
if (pendingLog.title === "Tool activity")
|
|
2374
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
1730
2375
|
}
|
|
1731
2376
|
process.off("SIGTERM", forwardSigterm);
|
|
1732
2377
|
if (attemptExit.error) {
|
|
1733
|
-
throw new
|
|
2378
|
+
throw new CliError(`Task run failed to start: ${attemptExit.error.message}`, 1);
|
|
1734
2379
|
}
|
|
1735
2380
|
const retryContext = resolveTaskRunRetryContext({
|
|
1736
2381
|
projectRoot: context.projectRoot,
|
|
@@ -1752,8 +2397,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1752
2397
|
currentPrompt = [retryContext.nextPrompt, "", "Rig Pi steering message:", retrySteering].join(`
|
|
1753
2398
|
`);
|
|
1754
2399
|
reviewFailureDetail = failureDetail;
|
|
2400
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "validating", { reason: "validation-retry" });
|
|
1755
2401
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
1756
|
-
status: "validating",
|
|
1757
2402
|
completedAt: null,
|
|
1758
2403
|
errorText: null
|
|
1759
2404
|
});
|
|
@@ -1777,7 +2422,8 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1777
2422
|
type: "status",
|
|
1778
2423
|
runId: input.runId,
|
|
1779
2424
|
status: "validating",
|
|
1780
|
-
detail: failureDetail
|
|
2425
|
+
detail: failureDetail,
|
|
2426
|
+
...sessionAnchor()
|
|
1781
2427
|
});
|
|
1782
2428
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Validate failed" });
|
|
1783
2429
|
emitServerRunEvent({ type: "log", runId: input.runId, title: `Steering Pi to retry validation (attempt ${attempt + 1}/${maxAttempts})` });
|
|
@@ -1788,10 +2434,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1788
2434
|
break;
|
|
1789
2435
|
}
|
|
1790
2436
|
if (!exit) {
|
|
1791
|
-
throw new
|
|
2437
|
+
throw new CliError(`Task run for ${runtimeTaskId} exhausted ${maxAttempts} attempts without a terminal result.`, 1);
|
|
1792
2438
|
}
|
|
1793
2439
|
if (exit.error) {
|
|
1794
|
-
throw new
|
|
2440
|
+
throw new CliError(`Task run failed to start: ${exit.error.message}`, 1);
|
|
1795
2441
|
}
|
|
1796
2442
|
if (assistantText.trim()) {
|
|
1797
2443
|
appendRunTimeline(context.projectRoot, input.runId, {
|
|
@@ -1818,9 +2464,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1818
2464
|
reviewAction.fail(failureDetail);
|
|
1819
2465
|
}
|
|
1820
2466
|
let terminalFailureDetail = failureDetail;
|
|
1821
|
-
const terminalRunStatus = reviewFailureDetail ? "
|
|
2467
|
+
const terminalRunStatus = reviewFailureDetail ? "needs-attention" : "failed";
|
|
1822
2468
|
try {
|
|
1823
|
-
await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "
|
|
2469
|
+
await updateTaskSourceAfterDriverRun(context.projectRoot, input.runId, input.taskId, sourceTask, "failed", terminalRunStatus === "needs-attention" ? "Rig task run needs attention." : "Rig task run failed.", {
|
|
1824
2470
|
latestRuntimeWorkspace,
|
|
1825
2471
|
latestLogsDir,
|
|
1826
2472
|
latestSessionDir,
|
|
@@ -1830,14 +2476,14 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
1830
2476
|
terminalFailureDetail = `${failureDetail}
|
|
1831
2477
|
Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1832
2478
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
completedAt: failedAt,
|
|
2479
|
+
setRunStatusOrFail(context.projectRoot, input.runId, terminalRunStatus, {
|
|
2480
|
+
reason: "driver-terminal-failure",
|
|
1836
2481
|
errorText: terminalFailureDetail
|
|
1837
2482
|
});
|
|
2483
|
+
patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
|
|
1838
2484
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1839
2485
|
id: `log:${input.runId}:${terminalRunStatus}`,
|
|
1840
|
-
title: terminalRunStatus === "
|
|
2486
|
+
title: terminalRunStatus === "needs-attention" ? "Task run needs attention" : "Task run failed",
|
|
1841
2487
|
detail: terminalFailureDetail,
|
|
1842
2488
|
tone: "error",
|
|
1843
2489
|
status: terminalRunStatus,
|
|
@@ -1848,19 +2494,19 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1848
2494
|
runId: input.runId,
|
|
1849
2495
|
error: terminalFailureDetail
|
|
1850
2496
|
});
|
|
1851
|
-
throw new
|
|
2497
|
+
throw new CliError(terminalFailureDetail, exit.code ?? 1);
|
|
1852
2498
|
}
|
|
1853
2499
|
if (planningClassification.planningRequired) {
|
|
1854
2500
|
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
1855
|
-
const expectedPlanPath =
|
|
1856
|
-
if (!
|
|
2501
|
+
const expectedPlanPath = resolve6(planWorkspace, planningArtifactPath);
|
|
2502
|
+
if (!existsSync4(expectedPlanPath)) {
|
|
1857
2503
|
const failedAt = new Date().toISOString();
|
|
1858
2504
|
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
completedAt: failedAt,
|
|
2505
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
|
|
2506
|
+
reason: "plan-artifact-missing",
|
|
1862
2507
|
errorText: failureDetail
|
|
1863
2508
|
});
|
|
2509
|
+
patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
|
|
1864
2510
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1865
2511
|
id: `log:${input.runId}:plan-artifact-missing`,
|
|
1866
2512
|
title: "Required plan artifact missing",
|
|
@@ -1870,8 +2516,66 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1870
2516
|
createdAt: failedAt
|
|
1871
2517
|
});
|
|
1872
2518
|
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
1873
|
-
throw new
|
|
2519
|
+
throw new CliError(failureDetail, 1);
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
if (process.env.RIG_SERVER_OWNS_CLOSEOUT === "1") {
|
|
2523
|
+
appendPiStageLog({
|
|
2524
|
+
projectRoot: context.projectRoot,
|
|
2525
|
+
runId: input.runId,
|
|
2526
|
+
stage: "Validate",
|
|
2527
|
+
detail: "Rig validation accepted the task run; server will continue PR/review/merge closeout.",
|
|
2528
|
+
status: "completed"
|
|
2529
|
+
});
|
|
2530
|
+
if (verificationAction && !reviewStarted) {
|
|
2531
|
+
verificationAction.complete("Completion verification checks finished.");
|
|
2532
|
+
}
|
|
2533
|
+
if (!reviewAction) {
|
|
2534
|
+
promoteToReviewing("Server-owned closeout is queued.");
|
|
1874
2535
|
}
|
|
2536
|
+
if (reviewAction) {
|
|
2537
|
+
reviewAction.complete("Provider work accepted; server-owned closeout requested.");
|
|
2538
|
+
}
|
|
2539
|
+
const requestedAt = new Date().toISOString();
|
|
2540
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "closing-out", { reason: "server-closeout-requested" });
|
|
2541
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
2542
|
+
completedAt: null,
|
|
2543
|
+
errorText: null,
|
|
2544
|
+
serverCloseout: {
|
|
2545
|
+
status: "pending",
|
|
2546
|
+
phase: "queued",
|
|
2547
|
+
updatedAt: requestedAt,
|
|
2548
|
+
runtimeWorkspace: latestRuntimeWorkspace,
|
|
2549
|
+
branch: latestRuntimeBranch,
|
|
2550
|
+
taskId: input.taskId ?? runtimeTaskId
|
|
2551
|
+
}
|
|
2552
|
+
});
|
|
2553
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
2554
|
+
id: `log:${input.runId}:server-closeout-requested`,
|
|
2555
|
+
title: "Server-owned closeout requested",
|
|
2556
|
+
detail: "The CLI provider worker finished validation and handed commit/PR/review/merge closeout back to the Rig server.",
|
|
2557
|
+
tone: "info",
|
|
2558
|
+
status: "reviewing",
|
|
2559
|
+
createdAt: requestedAt,
|
|
2560
|
+
payload: { runtimeWorkspace: latestRuntimeWorkspace, branch: latestRuntimeBranch }
|
|
2561
|
+
});
|
|
2562
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Server-owned closeout requested" });
|
|
2563
|
+
emitServerRunEvent({ type: "status", runId: input.runId, status: "closing-out", detail: "Server-owned closeout requested.", ...sessionAnchor() });
|
|
2564
|
+
await context.emitEvent("command.finished", {
|
|
2565
|
+
command: [
|
|
2566
|
+
"rig",
|
|
2567
|
+
"server",
|
|
2568
|
+
"task-run",
|
|
2569
|
+
...input.taskId ? ["--task", input.taskId] : [],
|
|
2570
|
+
...input.title ? ["--title", input.title] : []
|
|
2571
|
+
],
|
|
2572
|
+
formattedCommand: input.taskId ? `rig server task-run --task ${input.taskId}` : `rig server task-run --title ${JSON.stringify(input.title ?? `Run ${input.runId}`)}`,
|
|
2573
|
+
exitCode: 0,
|
|
2574
|
+
durationMs: 0,
|
|
2575
|
+
startedAt,
|
|
2576
|
+
finishedAt: requestedAt
|
|
2577
|
+
});
|
|
2578
|
+
process.exit(0);
|
|
1875
2579
|
}
|
|
1876
2580
|
const runPiPrFeedbackFix = async (message) => {
|
|
1877
2581
|
appendPiStageLog({
|
|
@@ -1900,11 +2604,45 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1900
2604
|
child.stdin.write(message);
|
|
1901
2605
|
}
|
|
1902
2606
|
child.stdin.end();
|
|
2607
|
+
const feedbackAssistantMessageId = `message:${input.runId}:pr-feedback:${Date.now()}:assistant`;
|
|
2608
|
+
let feedbackAssistantText = "";
|
|
2609
|
+
const feedbackPendingToolUses = new Map;
|
|
1903
2610
|
const stdout = createLineInterface({ input: child.stdout });
|
|
1904
2611
|
stdout.on("line", (line) => {
|
|
1905
2612
|
const trimmed = line.trim();
|
|
1906
2613
|
if (!trimmed)
|
|
1907
2614
|
return;
|
|
2615
|
+
try {
|
|
2616
|
+
const record = JSON.parse(trimmed);
|
|
2617
|
+
const providerLogs = buildClaudeLogsFromRecord({
|
|
2618
|
+
runId: input.runId,
|
|
2619
|
+
record,
|
|
2620
|
+
createdAtFallback: new Date().toISOString(),
|
|
2621
|
+
status: "reviewing",
|
|
2622
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2623
|
+
});
|
|
2624
|
+
for (const providerLog of providerLogs) {
|
|
2625
|
+
appendRunLog(context.projectRoot, input.runId, providerLog);
|
|
2626
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: providerLog });
|
|
2627
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: providerLog.title });
|
|
2628
|
+
if (providerLog.title === "Tool activity")
|
|
2629
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2630
|
+
}
|
|
2631
|
+
const nextFeedbackAssistantText = appendAssistantTimelineFromRecord({
|
|
2632
|
+
projectRoot: context.projectRoot,
|
|
2633
|
+
runId: input.runId,
|
|
2634
|
+
messageId: feedbackAssistantMessageId,
|
|
2635
|
+
record,
|
|
2636
|
+
assistantText: feedbackAssistantText
|
|
2637
|
+
});
|
|
2638
|
+
const hadAssistantDelta = nextFeedbackAssistantText !== feedbackAssistantText;
|
|
2639
|
+
if (hadAssistantDelta) {
|
|
2640
|
+
feedbackAssistantText = nextFeedbackAssistantText;
|
|
2641
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2642
|
+
}
|
|
2643
|
+
if (providerLogs.length > 0 || hadAssistantDelta)
|
|
2644
|
+
return;
|
|
2645
|
+
} catch {}
|
|
1908
2646
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1909
2647
|
id: nextRunLogId(),
|
|
1910
2648
|
title: "Pi PR feedback fix output",
|
|
@@ -1930,10 +2668,31 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1930
2668
|
});
|
|
1931
2669
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
1932
2670
|
});
|
|
1933
|
-
const exitCode = await new Promise((
|
|
1934
|
-
child.once("error", () =>
|
|
1935
|
-
child.once("close", (code) =>
|
|
2671
|
+
const exitCode = await new Promise((resolve7) => {
|
|
2672
|
+
child.once("error", () => resolve7(1));
|
|
2673
|
+
child.once("close", (code) => resolve7(code ?? 1));
|
|
1936
2674
|
});
|
|
2675
|
+
for (const pendingLog of flushPendingClaudeToolUseLogs({
|
|
2676
|
+
runId: input.runId,
|
|
2677
|
+
status: exitCode === 0 ? "completed" : "failed",
|
|
2678
|
+
pendingToolUses: feedbackPendingToolUses
|
|
2679
|
+
})) {
|
|
2680
|
+
appendRunLog(context.projectRoot, input.runId, pendingLog);
|
|
2681
|
+
appendToolTimelineFromLog({ projectRoot: context.projectRoot, runId: input.runId, log: pendingLog });
|
|
2682
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: pendingLog.title });
|
|
2683
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2684
|
+
}
|
|
2685
|
+
if (feedbackAssistantText.trim()) {
|
|
2686
|
+
appendRunTimeline(context.projectRoot, input.runId, {
|
|
2687
|
+
id: feedbackAssistantMessageId,
|
|
2688
|
+
type: "assistant_message",
|
|
2689
|
+
text: feedbackAssistantText,
|
|
2690
|
+
state: "completed",
|
|
2691
|
+
createdAt: new Date().toISOString(),
|
|
2692
|
+
completedAt: new Date().toISOString()
|
|
2693
|
+
});
|
|
2694
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
2695
|
+
}
|
|
1937
2696
|
if (exitCode !== 0) {
|
|
1938
2697
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
1939
2698
|
}
|
|
@@ -1969,25 +2728,25 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1969
2728
|
const failureDetail = postValidation.pr?.actionableFeedback.join(`
|
|
1970
2729
|
`) || "PR automation needs attention.";
|
|
1971
2730
|
const failedAt = new Date().toISOString();
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
completedAt: failedAt,
|
|
2731
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "needs-attention", {
|
|
2732
|
+
reason: "pr-automation-needs-attention",
|
|
1975
2733
|
errorText: failureDetail
|
|
1976
2734
|
});
|
|
2735
|
+
patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
|
|
1977
2736
|
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
1978
|
-
throw new
|
|
2737
|
+
throw new CliError(failureDetail, 1);
|
|
1979
2738
|
}
|
|
1980
2739
|
} catch (error) {
|
|
1981
|
-
if (error instanceof
|
|
2740
|
+
if (error instanceof CliError) {
|
|
1982
2741
|
throw error;
|
|
1983
2742
|
}
|
|
1984
2743
|
const failureDetail = `Failed to complete PR/issue closeout for ${input.taskId ?? runtimeTaskId}: ${error instanceof Error ? error.message : String(error)}`;
|
|
1985
2744
|
const failedAt = new Date().toISOString();
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
completedAt: failedAt,
|
|
2745
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "failed", {
|
|
2746
|
+
reason: "pr-closeout-failed",
|
|
1989
2747
|
errorText: failureDetail
|
|
1990
2748
|
});
|
|
2749
|
+
patchAuthorityRun(context.projectRoot, input.runId, { completedAt: failedAt });
|
|
1991
2750
|
appendRunLog(context.projectRoot, input.runId, {
|
|
1992
2751
|
id: `log:${input.runId}:pr-closeout-failed`,
|
|
1993
2752
|
title: "PR/issue closeout failed",
|
|
@@ -1997,7 +2756,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
1997
2756
|
createdAt: failedAt
|
|
1998
2757
|
});
|
|
1999
2758
|
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
2000
|
-
throw new
|
|
2759
|
+
throw new CliError(failureDetail, 1);
|
|
2001
2760
|
}
|
|
2002
2761
|
if (verificationAction && !reviewStarted) {
|
|
2003
2762
|
verificationAction.complete("Completion verification checks finished.");
|
|
@@ -2008,10 +2767,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
2008
2767
|
if (reviewAction) {
|
|
2009
2768
|
reviewAction.complete("Run marked completed.");
|
|
2010
2769
|
}
|
|
2011
|
-
|
|
2012
|
-
status: "completed",
|
|
2013
|
-
completedAt: new Date().toISOString()
|
|
2014
|
-
});
|
|
2770
|
+
setRunStatusOrFail(context.projectRoot, input.runId, "completed", { reason: "driver-completed" });
|
|
2015
2771
|
emitServerRunEvent({ type: "completed", runId: input.runId });
|
|
2016
2772
|
await context.emitEvent("command.finished", {
|
|
2017
2773
|
command: [
|