fix: add bounded AgentRun task input drill-down

This commit is contained in:
Codex
2026-07-12 05:34:05 +02:00
parent 3b9dc4c3ef
commit 2680ef6462
8 changed files with 308 additions and 6 deletions
+4 -1
View File
@@ -57,6 +57,7 @@ export function agentRunHelp(): unknown {
"bun scripts/cli.ts agentrun get tasks -o wide",
"bun scripts/cli.ts agentrun get tasks -o json",
"bun scripts/cli.ts agentrun describe task/<taskId>",
"bun scripts/cli.ts agentrun describe task/<taskId> --input -o json",
"bun scripts/cli.ts agentrun events run/<runId> --after-seq 0 --limit 100",
"bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100",
"bun scripts/cli.ts agentrun result run/<runId> --command <commandId>",
@@ -272,11 +273,12 @@ export function agentRunHelpText(args: string[]): string {
}
if (verb === "describe") {
return [
"Usage: bun scripts/cli.ts agentrun describe <kind/name> [--node <node> --lane <lane>] [--full] [-o json|yaml] [--raw]",
"Usage: bun scripts/cli.ts agentrun describe <kind/name> [--node <node> --lane <lane>] [--input] [--full] [-o json|yaml] [--raw]",
"",
"Kinds: task|run|command|runnerjob|session|aipodspec",
"Examples:",
" bun scripts/cli.ts agentrun describe task/qt_...",
" bun scripts/cli.ts agentrun describe task/qt_... --input -o json",
" bun scripts/cli.ts agentrun describe run/<runId> --node NC01 --lane nc01-v02",
" bun scripts/cli.ts agentrun describe command/cmd_... --run <runId>",
" bun scripts/cli.ts agentrun describe session/<sessionId> --full",
@@ -391,6 +393,7 @@ export function agentRunHelpText(args: string[]): string {
"Common:",
" bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",
" bun scripts/cli.ts agentrun describe task/<taskId>",
" bun scripts/cli.ts agentrun describe task/<taskId> --input -o json",
" bun scripts/cli.ts agentrun describe run/<runId> --node NC01 --lane nc01-v02",
" bun scripts/cli.ts agentrun events run/<runId> --after-seq 0 --limit 100",
" bun scripts/cli.ts agentrun logs session/<sessionId> --tail 100",
+137 -1
View File
@@ -1335,12 +1335,13 @@ export function renderTaskDescription(task: Record<string, unknown>, options: Ag
"Next:",
];
const next = [
`bun scripts/cli.ts agentrun describe task/${taskId} --input -o json`,
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}`,
`bun scripts/cli.ts agentrun ack task/${taskId}`,
options.full ? null : `bun scripts/cli.ts agentrun describe task/${taskId} --full`,
].filter((item): item is string => item !== null).slice(0, 5);
].filter((item): item is string => item !== null).slice(0, 6);
lines.push(...next.map((item) => ` ${item}`));
return lines.join("\n");
}
@@ -1379,12 +1380,147 @@ export function compactTaskDescriptionPayload(ref: AgentRunResourceRef, task: Re
logs: sessionId === null ? null : `bun scripts/cli.ts agentrun logs session/${sessionId} --tail 100`,
result: runId === null || commandId === null ? null : `bun scripts/cli.ts agentrun result run/${runId} --command ${commandId}`,
ack: `bun scripts/cli.ts agentrun ack task/${taskId}`,
input: `bun scripts/cli.ts agentrun describe task/${taskId} --input -o json`,
full: `bun scripts/cli.ts agentrun describe task/${taskId} --full -o json`,
},
valuesPrinted: false,
};
}
export function taskInputDescriptionPayload(ref: AgentRunResourceRef, task: Record<string, unknown>): Record<string, unknown> {
const metadata = taskInputMetadata(task.metadata);
const aipodImageRef = taskInputImageRef(metadata.aipodImageRef);
if (aipodImageRef !== null) metadata.aipodImageRef = aipodImageRef;
const spec = pickCompact(task, [
"tenantId",
"projectId",
"queue",
"lane",
"title",
"priority",
"backendProfile",
"providerId",
"payload",
"references",
"idempotencyKey",
]);
spec.workspaceRef = taskInputWorkspaceRef(task.workspaceRef);
spec.sessionRef = taskInputSessionRef(task.sessionRef);
spec.executionPolicy = taskInputExecutionPolicy(task.executionPolicy);
spec.resourceBundleRef = taskInputResourceBundleRef(task.resourceBundleRef);
spec.metadata = metadata;
return {
kind: "TaskInput",
name: ref.name,
spec,
aipodSpec: {
name: metadata.aipod ?? null,
specHash: metadata.aipodSpecHash ?? null,
imageRef: aipodImageRef,
valuesPrinted: false,
},
provenance: {
sourceTaskId: stringOrNull(task.id) ?? ref.name,
source: metadata.source ?? null,
payloadHash: task.payloadHash ?? null,
valuesPrinted: false,
},
redaction: {
credentialValues: "omitted",
secretRefsOnly: true,
valuesPrinted: false,
},
valuesPrinted: false,
};
}
function taskInputExecutionPolicy(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
const policy = pickCompact(value, ["sandbox", "approval", "timeoutMs", "network"]);
const secretScope = record(value.secretScope);
policy.secretScope = {
allowCredentialEcho: false,
providerCredentials: arrayRecords(secretScope.providerCredentials).map((credential) => ({
...pickCompact(credential, ["profile"]),
secretRef: taskInputSecretRef(credential.secretRef),
valuesPrinted: false,
})),
toolCredentials: arrayRecords(secretScope.toolCredentials).map((credential) => ({
...pickCompact(credential, ["tool", "purpose"]),
secretRef: taskInputSecretRef(credential.secretRef),
projection: pickCompact(record(credential.projection), ["kind", "envName", "secretKey", "mountPath"]),
valuesPrinted: false,
})),
valuesPrinted: false,
};
return policy;
}
function taskInputSecretRef(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
return {
...pickCompact(value, ["namespace", "name", "keys", "mountPath"]),
valuesPrinted: false,
};
}
function taskInputResourceBundleRef(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
const result = pickCompact(value, ["kind", "commitId", "ref", "submodules", "lfs"]);
result.repoUrl = taskInputRepoUrl(value.repoUrl);
result.bundles = arrayRecords(value.bundles).map((bundle) => ({
...pickCompact(bundle, ["name", "commitId", "ref", "subpath", "targetPath"]),
...(bundle.repoUrl === undefined ? {} : { repoUrl: taskInputRepoUrl(bundle.repoUrl) }),
}));
if (Array.isArray(value.promptRefs)) {
result.promptRefs = arrayRecords(value.promptRefs).map((item) => pickCompact(item, ["name", "path", "inject", "required"]));
}
if (Array.isArray(value.requiredSkills)) {
result.requiredSkills = arrayRecords(value.requiredSkills).map((item) => pickCompact(item, ["name"]));
}
if (value.credentialRef !== undefined) result.credentialRef = taskInputSecretRef(value.credentialRef);
result.valuesPrinted = false;
return result;
}
function taskInputWorkspaceRef(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
const result = pickCompact(value, ["kind", "path", "branch"]);
if (value.repo !== undefined) result.repo = taskInputRepoUrl(value.repo);
return result;
}
function taskInputSessionRef(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
return pickCompact(value, ["sessionId", "conversationId", "threadId", "expiresAt"]);
}
function taskInputMetadata(value: unknown): Record<string, unknown> {
if (!isRecord(value)) return {};
return pickCompact(value, ["aipod", "source", "aipodSpecHash", "aipodImageRef"]);
}
function taskInputImageRef(value: unknown): Record<string, unknown> | null {
if (!isRecord(value)) return null;
const result = pickCompact(value, ["kind", "commitId", "dockerfilePath", "sourceIdentity"]);
if (value.repoUrl !== undefined) result.repoUrl = taskInputRepoUrl(value.repoUrl);
result.valuesPrinted = false;
return result;
}
function taskInputRepoUrl(value: unknown): unknown {
if (typeof value !== "string") return value ?? null;
if (!value.includes("://")) return value;
try {
const parsed = new URL(value);
parsed.username = "";
parsed.password = "";
return parsed.toString();
} catch {
return "<invalid-or-redacted-url>";
}
}
export function unwrapTaskDetail(data: Record<string, unknown>): Record<string, unknown> {
const task = record(data.task);
if (Object.keys(task).length > 0) return task;
+12 -4
View File
@@ -37,7 +37,7 @@ import type { AgentRunResourceOptions, AgentRunResourceRef, AgentRunResourceVerb
import { agentRunDryRunPlan } from "./config";
import { isHelpArg, renderAgentRunHelp } from "./entry";
import { agentRunExplain, arrayRecords, shortId } from "./options";
import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderDescribe, renderEventLike, renderGenericDescription, renderMachine, renderMutationSummary, renderQueueAttemptList, renderQueueRetrySummary, renderResourceResult, renderResultSummary, renderTaskDescription, renderedCliResult, requiredContext, rerunWithoutDryRun, resolveAgentRunCancelPolicyTarget, resourceApply, resourceCreate, resourceDispatch, resourceSessionPromptCommand, taskListState, unwrapTaskDetail } from "./render";
import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderDescribe, renderEventLike, renderGenericDescription, renderMachine, renderMutationSummary, renderQueueAttemptList, renderQueueRetrySummary, renderResourceResult, renderResultSummary, renderTaskDescription, renderedCliResult, requiredContext, rerunWithoutDryRun, resolveAgentRunCancelPolicyTarget, resourceApply, resourceCreate, resourceDispatch, resourceSessionPromptCommand, taskInputDescriptionPayload, taskListState, unwrapTaskDetail } from "./render";
import { AgentRunRestError, renderAgentRunRestError, resolveAgentRunRestTarget, runAgentRunRestCommand, withAgentRunRestTarget } from "./rest-bridge";
import { runnerJobObservationScript, type RunnerJobObservationIdentity } from "./runner-observation";
import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, record, stringOrNull } from "./utils";
@@ -121,7 +121,7 @@ export function stripAgentRunResourceWrapperArgs(args: string[]): string[] {
}
if (arg.startsWith("--output=") || arg.startsWith("-o=")) continue;
if (arg.startsWith("--node=") || arg.startsWith("--lane=")) continue;
if (arg === "--full" || arg === "--raw" || arg === "--debug" || arg === "--full-text") continue;
if (arg === "--full" || arg === "--raw" || arg === "--debug" || arg === "--full-text" || arg === "--input") continue;
result.push(arg);
}
return result;
@@ -133,6 +133,7 @@ export function parseResourceOptions(args: string[]): AgentRunResourceOptions {
full: false,
raw: false,
debug: false,
taskInput: false,
limit: 20,
cursor: null,
queue: null,
@@ -157,7 +158,7 @@ export function parseResourceOptions(args: string[]): AgentRunResourceOptions {
passthroughArgs: [],
};
const valueFlags = new Set(["-o", "--output", "--limit", "--cursor", "--queue", "--state", "--reader-id", "--task", "--task-id", "--run", "--run-id", "--command", "--command-id", "--session", "--session-id", "--after-seq", "--tail", "--reason", "-f", "--file", "--filename", "--aipod", "--idempotency-key", "--node", "--lane"]);
const booleanFlags = new Set(["--full", "--raw", "--debug", "--unread", "--dry-run", "--full-text", "--prompt-stdin", "--stdin"]);
const booleanFlags = new Set(["--full", "--raw", "--debug", "--input", "--unread", "--dry-run", "--full-text", "--prompt-stdin", "--stdin"]);
for (let index = 0; index < args.length; index += 1) {
const arg = args[index] ?? "";
if (!arg.startsWith("-")) continue;
@@ -190,6 +191,7 @@ export function applyResourceOption(options: AgentRunResourceOptions, flag: stri
if (flag === "--full") options.full = true;
else if (flag === "--raw") options.raw = true;
else if (flag === "--debug") options.debug = true;
else if (flag === "--input") options.taskInput = true;
else if (flag === "--unread") options.unread = true;
else if (flag === "--dry-run") options.dryRun = true;
else if (flag === "--full-text") options.fullText = true;
@@ -303,15 +305,21 @@ export async function resourceRetry(config: UniDeskConfig | null, command: strin
export async function resourceDescribe(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
const ref = parseResourceRef(action, args);
if (options.taskInput && ref.kind !== "task") throw new AgentRunRestError("validation-failed", "describe --input supports task/<taskId> only");
if (ref.kind === "task") {
const result = await runAgentRunRestCommand(config, "queue", ["show", ref.name, ...(options.full ? ["--full"] : [])]);
const data = record(innerData(result));
const task = unwrapTaskDetail(data);
if (options.raw) return renderMachine(command, result, "json", result.ok !== false);
if (options.output === "json" || options.output === "yaml") {
const payload = options.full ? { kind: ref.kind, name: ref.name, resource: task } : compactTaskDescriptionPayload(ref, task);
const payload = options.full
? { kind: ref.kind, name: ref.name, resource: task }
: options.taskInput
? taskInputDescriptionPayload(ref, task)
: compactTaskDescriptionPayload(ref, task);
return renderMachine(command, payload, options.output, result.ok !== false);
}
if (options.taskInput && !options.full) return renderMachine(command, taskInputDescriptionPayload(ref, task), "json", result.ok !== false);
return renderedCliResult(result.ok !== false, command, renderTaskDescription(task, options));
}
if (ref.kind === "run") {
+1
View File
@@ -64,6 +64,7 @@ export interface AgentRunResourceOptions {
full: boolean;
raw: boolean;
debug: boolean;
taskInput: boolean;
limit: number;
cursor: number | null;
queue: string | null;