fix: surface AgentRun resource failures in human output
This commit is contained in:
@@ -81,9 +81,7 @@ assertCondition(
|
||||
agentRunSource.includes('type AgentRunBridgeCaptureBackend = "local-backend-core-broker" | "remote-frontend-websocket"')
|
||||
&& agentRunSource.includes('reason: "runner-environment"')
|
||||
&& agentRunSource.includes('degradedReason: "capture-backend-unavailable"')
|
||||
&& agentRunSource.includes('"agentrun-cli-returned-failure"')
|
||||
&& agentRunSource.includes('failureKind: "bridge-execution-environment"')
|
||||
&& agentRunSource.includes('key === "nextActions"'),
|
||||
&& agentRunSource.includes('failureKind: "bridge-execution-environment"'),
|
||||
"AgentRun CLI bridge must use the remote frontend backend in runner/no-Docker environments and classify bridge failures separately",
|
||||
);
|
||||
|
||||
@@ -100,13 +98,23 @@ assertCondition(
|
||||
);
|
||||
|
||||
assertCondition(
|
||||
agentRunSource.includes('if (verb === "dispatch") return await resourceDispatch(config, command, action, actionArgs, options);')
|
||||
&& agentRunSource.includes('runOfficialAgentRunCli(config, "queue", ["dispatch", ref.name')
|
||||
&& agentRunSource.includes('"list", "--state", taskListState(options)')
|
||||
agentRunSource.includes('if (verb === "dispatch") return await resourceDispatch')
|
||||
&& agentRunSource.includes('bridgeActionArgs, options')
|
||||
&& agentRunSource.includes('runAgentRunRestCommand(config, "queue", ["dispatch"')
|
||||
&& agentRunSource.includes('taskListState(options)')
|
||||
&& agentRunSource.includes('["commander", "--reader-id", options.readerId'),
|
||||
"AgentRun resources must wrap task dispatch and keep default get tasks on active list visibility",
|
||||
);
|
||||
|
||||
assertCondition(
|
||||
agentRunSource.includes("function renderFailureLines(value: Record<string, unknown>): string[]")
|
||||
&& agentRunSource.includes('lines.push(`Failure: ${failureKind}`)')
|
||||
&& agentRunSource.includes('lines.push(`Message: ${failureMessage}`)')
|
||||
&& agentRunSource.includes("const failure = renderFailureLines(data);")
|
||||
&& agentRunSource.includes("const failure = renderFailureLines(value);"),
|
||||
"AgentRun resource human output must expose failure kind and message without requiring JSON output",
|
||||
);
|
||||
|
||||
assertCondition(
|
||||
agentRunSource.includes("const effectiveLimit = options.tail ?? options.limit;")
|
||||
&& agentRunSource.includes("return renderEventLike(command, result, { ...options, limit: effectiveLimit }, \"Log\""),
|
||||
@@ -134,6 +142,7 @@ console.log(JSON.stringify({
|
||||
"AgentRun CLI bridge keeps AgentRun failures distinct from bridge failures",
|
||||
"AgentRun resource parser supports apply -f -",
|
||||
"AgentRun resource task dispatch and active task list visibility",
|
||||
"AgentRun resource failure output is visible in human mode",
|
||||
"AgentRun logs tail controls page limit",
|
||||
"AgentRun dry-run mutations keep resource-command follow-up",
|
||||
],
|
||||
|
||||
+89
-4
@@ -622,7 +622,10 @@ function renderResultSummary(command: string, raw: Record<string, unknown>, opti
|
||||
];
|
||||
const final = stringOrNull(data.finalResponse) ?? stringOrNull(data.output) ?? stringOrNull(data.summary) ?? stringOrNull(data.result);
|
||||
if (final !== null) lines.push("", truncateMultiline(final, options.fullText ? 8000 : 1600));
|
||||
else lines.push("", JSON.stringify(pickCompact(data, ["failureKind", "terminalClassification", "degradedReason", "message", "valuesPrinted"]), null, 2));
|
||||
else {
|
||||
const failure = renderFailureLines(data);
|
||||
lines.push("", ...(failure.length > 0 ? failure : ["No final response is available yet."]));
|
||||
}
|
||||
return renderedCliResult(raw.ok !== false, command, lines.join("\n"));
|
||||
}
|
||||
|
||||
@@ -921,13 +924,95 @@ function renderGenericDescription(ref: AgentRunResourceRef, data: unknown): stri
|
||||
const lines = [
|
||||
`Name: ${ref.kind}/${ref.name}`,
|
||||
`State: ${displayValue(value.state ?? value.status ?? value.phase ?? "-")}`,
|
||||
"",
|
||||
"Summary:",
|
||||
JSON.stringify(pickCompact(value, ["id", "name", "runId", "commandId", "runnerJobId", "sessionId", "state", "status", "phase", "reason", "failureKind", "terminalClassification", "valuesPrinted"]), null, 2),
|
||||
];
|
||||
const identity = renderResourceIdentityLines(ref, value);
|
||||
if (identity.length > 0) lines.push("", ...identity);
|
||||
const failure = renderFailureLines(value);
|
||||
if (failure.length > 0) lines.push("", ...failure);
|
||||
const next = renderResourceNextLines(ref, value);
|
||||
if (next.length > 0) lines.push("", "Next:", ...next.map((line) => ` ${line}`));
|
||||
if (identity.length === 0 && failure.length === 0 && next.length === 0) {
|
||||
lines.push(
|
||||
"",
|
||||
"Summary:",
|
||||
JSON.stringify(pickCompact(value, ["id", "name", "runId", "commandId", "runnerJobId", "sessionId", "state", "status", "phase", "reason", "failureKind", "valuesPrinted"]), null, 2),
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function renderResourceIdentityLines(ref: AgentRunResourceRef, value: Record<string, unknown>): string[] {
|
||||
const fields: [string, unknown][] = [
|
||||
["Run", value.runId],
|
||||
["Command", value.commandId],
|
||||
["RunnerJob", value.runnerJobId ?? (ref.kind === "runnerjob" ? value.id : null)],
|
||||
["Session", value.sessionId],
|
||||
["Namespace", value.namespace],
|
||||
["Job", value.jobName],
|
||||
["Image", value.image],
|
||||
["LogPath", value.logPath],
|
||||
["Started", value.startedAt],
|
||||
["Finished", value.finishedAt],
|
||||
];
|
||||
if (ref.kind !== "runnerjob") {
|
||||
return fields
|
||||
.filter(([label]) => label === "Run" || label === "Command" || label === "RunnerJob" || label === "Session")
|
||||
.map(([label, item]) => formatResourceField(label, item))
|
||||
.filter((line): line is string => line !== null);
|
||||
}
|
||||
return fields.map(([label, item]) => formatResourceField(label, item)).filter((line): line is string => line !== null);
|
||||
}
|
||||
|
||||
function formatResourceField(label: string, value: unknown): string | null {
|
||||
const text = stringOrNull(value);
|
||||
if (text === null || text.length === 0) return null;
|
||||
return `${label}: ${text}`;
|
||||
}
|
||||
|
||||
function renderFailureLines(value: Record<string, unknown>): string[] {
|
||||
const terminalClassification = record(value.terminalClassification);
|
||||
const failureKind = stringOrNull(value.failureKind)
|
||||
?? stringOrNull(terminalClassification.failureKind)
|
||||
?? stringOrNull(value.degradedReason);
|
||||
const failureMessage = stringOrNull(value.failureMessage)
|
||||
?? stringOrNull(terminalClassification.failureMessage)
|
||||
?? stringOrNull(value.message)
|
||||
?? stringOrNull(value.reason);
|
||||
const category = stringOrNull(terminalClassification.category);
|
||||
const timeoutKind = stringOrNull(terminalClassification.timeoutKind);
|
||||
const timeoutState = stringOrNull(terminalClassification.timeoutState);
|
||||
const providerInterruption = stringOrNull(terminalClassification.providerInterruption);
|
||||
const lastActivity = stringOrNull(terminalClassification.lastActivityKind);
|
||||
const lastActivitySeq = terminalClassification.lastActivitySeq;
|
||||
const lines: string[] = [];
|
||||
if (failureKind !== null) lines.push(`Failure: ${failureKind}`);
|
||||
if (failureMessage !== null) lines.push(`Message: ${failureMessage}`);
|
||||
if (category !== null) lines.push(`Category: ${category}`);
|
||||
if (timeoutKind !== null || timeoutState !== null) lines.push(`Timeout: ${displayValue(timeoutKind)} / ${displayValue(timeoutState)}`);
|
||||
if (providerInterruption !== null) lines.push(`Provider: ${providerInterruption}`);
|
||||
if (lastActivity !== null) lines.push(`LastActivity: ${lastActivity}${lastActivitySeq === undefined ? "" : ` seq=${String(lastActivitySeq)}`}`);
|
||||
return lines;
|
||||
}
|
||||
|
||||
function renderResourceNextLines(ref: AgentRunResourceRef, value: Record<string, unknown>): string[] {
|
||||
const runId = stringOrNull(value.runId);
|
||||
const commandId = stringOrNull(value.commandId);
|
||||
const runnerJobId = stringOrNull(value.runnerJobId) ?? (ref.kind === "runnerjob" ? ref.name : null);
|
||||
const sessionId = stringOrNull(value.sessionId);
|
||||
const lines = [
|
||||
runId === null ? null : `bun scripts/cli.ts agentrun events run/${runId} --after-seq 0 --limit 100`,
|
||||
sessionId === null ? null : `bun scripts/cli.ts agentrun logs session/${sessionId} --tail 100`,
|
||||
runId === null || commandId === null ? null : `bun scripts/cli.ts agentrun result run/${runId} --command ${commandId}`,
|
||||
runId === null || commandId === null ? null : `bun scripts/cli.ts agentrun describe command/${commandId} --run ${runId} --full`,
|
||||
runId === null || runnerJobId === null ? null : `bun scripts/cli.ts agentrun describe runnerjob/${runnerJobId} --run ${runId} --full`,
|
||||
];
|
||||
const unique: string[] = [];
|
||||
for (const line of lines) {
|
||||
if (line !== null && !unique.includes(line)) unique.push(line);
|
||||
}
|
||||
return unique.slice(0, 5);
|
||||
}
|
||||
|
||||
function parseTaskManifest(raw: string, source: string): Record<string, unknown> {
|
||||
const parsed = source.endsWith(".json") ? JSON.parse(raw) as unknown : Bun.YAML.parse(raw) as unknown;
|
||||
const input = record(parsed);
|
||||
|
||||
Reference in New Issue
Block a user