// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. render module for scripts/src/agentrun.ts. // Moved mechanically from scripts/src/agentrun.ts:687-1652 for #903. // SPEC: PJ2026-01020108 cancel lifecycle + PJ2026-01020205 AipodSpec binding + PJ2026-01020302 session policy + PJ2026-01020305 cancel control + PJ2026-01060305/06 YAML execution policy and bounded output draft-2026-06-25-p0. // Exposes AgentRun lane-scoped policy, AipodSpec SecretRef binding, cancel lifecycle, and bounded default output in the UniDesk CLI. import { chmodSync, copyFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { spawnSync } from "node:child_process"; import { rootPath, type UniDeskConfig } from "../config"; import type { RenderedCliResult } from "../output"; import { applyLocalCaddyManagedSite } from "../pk01-caddy"; import { runSshCommandCapture, type SshCaptureResult } from "../ssh"; import { runRemoteSshCommandCapture } from "../remote"; import { startJob } from "../jobs"; import { AGENTRUN_CONFIG_PATH, agentRunLaneSummary, agentRunPipelineRunName, agentRunProviderCredentialRefs, resolveAgentRunLaneTarget, type AgentRunCancelLifecycleSpec, type AgentRunLaneSpec, } from "../agentrun-lanes"; import { agentRunImageArtifact, placeholderAgentRunImage, renderedFilesDigest, renderedObjectsDigest, renderAgentRunControlPlaneManifests, renderAgentRunGitopsFiles, type AgentRunArtifactService, } from "../agentrun-manifests"; import { sha256Fingerprint } from "../platform-infra-ops-library"; import type { AgentRunResourceKind, AgentRunResourceOptions, AgentRunResourceRef } from "./utils"; import { agentRunDryRunPlan, readAgentRunClientConfig } from "./config"; import { status } from "./control-plane"; import { arrayRecords, displayValue, isRecord, nextPagedResourceCommand, parseTaskManifest, pickCompact, relativeAge, renderFailureLines, renderResourceNextLines, renderTable, resourceName, shortId, stringOrDash, truncateMultiline, truncateOneLine } from "./options"; import { activeAgentRunRestTarget, agentRunRestRequest, runAgentRunRestCommand } from "./rest-bridge"; import { record, stringOrNull } from "./utils"; export function resolveAgentRunCancelPolicyTarget(config: UniDeskConfig | null, options: AgentRunResourceOptions): { configPath: string; spec: AgentRunLaneSpec; source: "selected-lane" | "default-lane" } | null { if (activeAgentRunRestTarget !== null) return { configPath: activeAgentRunRestTarget.configPath, spec: activeAgentRunRestTarget.spec, source: "selected-lane" }; if (config === null) return null; const { configPath, spec } = resolveAgentRunLaneTarget({ node: options.node, lane: options.lane }); return { configPath, spec, source: options.node !== null || options.lane !== null ? "selected-lane" : "default-lane" }; } export function agentRunCancelAuthorityDisclosure(target: { configPath: string; spec: AgentRunLaneSpec; source: "selected-lane" | "default-lane" } | null): Record { const laneTarget = activeAgentRunRestTarget !== null; return { transport: laneTarget ? "lane-k8s-service-proxy" : "direct-http", policySource: target?.source ?? "unavailable", node: target?.spec.nodeId ?? null, lane: target?.spec.lane ?? null, namespace: target?.spec.runtime.namespace ?? null, managerDeployment: target?.spec.runtime.managerDeployment ?? null, baseUrl: laneTarget ? target?.spec.runtime.internalBaseUrl ?? null : agentRunDirectManagerBaseUrl(), laneConfigPath: target?.configPath ?? null, valuesPrinted: false, }; } export function agentRunDirectManagerBaseUrl(): string | null { try { return readAgentRunClientConfig().manager.baseUrl; } catch { return null; } } export function agentRunCancelRunnerAbortDisclosure(policy: AgentRunCancelLifecycleSpec): Record { return { deliveryMode: policy.deliveryMode, gracefulAbortMs: policy.gracefulAbortMs, killEscalationMs: policy.killEscalationMs, valuesPrinted: false, }; } export function agentRunCancelFencingDisclosure(policy: AgentRunCancelLifecycleSpec | null): Record { if (policy === null) return { cancelEpoch: true, policySource: "unavailable", valuesPrinted: false }; return { cancelEpoch: true, staleHeartbeatFencingMs: policy.staleHeartbeatFencingMs, lateWriteFencing: policy.lateWriteFencing.enabled, valuesPrinted: false, }; } export function agentRunCancelCascadeScope(kind: AgentRunResourceKind): string { if (kind === "task") return "current task attempt -> run -> active command -> runner job"; if (kind === "session") return "session active work -> active run/command -> session-scoped background work"; if (kind === "run") return "run active commands -> runner jobs -> run terminal"; if (kind === "command") return "single command -> current runner job; session remains reusable"; return "unsupported cancel target"; } export async function resourceDispatch(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise { const ref = parseResourceRef(action, args, "task"); if (ref.kind !== "task") throw new Error("dispatch supports task/"); const result = await runAgentRunRestCommand(config, "queue", ["dispatch", ref.name, ...stripLeadingResource(args, ref.name)]); return renderMutationSummary(command, result, options, `Task dispatch submitted ${shortId(ref.name)}`, [ `bun scripts/cli.ts agentrun describe task/${ref.name}`, ]); } export async function resourceCreate(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise { const kind = parseResourceKind(action); if (kind !== "task") throw new Error("create currently supports: create task"); const submitArgs = ["submit", ...stripLeadingResource(args, "task")]; const result = await runAgentRunRestCommand(config, "queue", submitArgs); return renderMutationSummary(command, result, options, options.dryRun ? "Task create plan" : "Task create submitted", options.dryRun ? [rerunWithoutDryRun(command)] : undefined); } export async function resourceApply(config: UniDeskConfig | null, command: string, args: string[], options: AgentRunResourceOptions): Promise { void config; const file = options.file ?? requiredContext("apply", "-f |-"); const raw = file === "-" ? readFileSync(0, "utf8") : readFileSync(file, "utf8"); const parsed = parseTaskManifest(raw, file); const result = options.dryRun ? agentRunDryRunPlan("queue-submit", "/api/v1/queue/tasks", parsed, `bun scripts/cli.ts ${command.replace(/\s+--dry-run\b/gu, "").trim()}`) : await agentRunRestRequest("agentrun queue submit", "POST", "/api/v1/queue/tasks", parsed); return renderMutationSummary(command, result, options, `${options.dryRun ? "Dry-run applied" : "Applied"} task manifest`, options.dryRun ? [rerunWithoutDryRun(command)] : undefined); } export async function resourceSessionPromptCommand(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise { const ref = parseResourceRef(action, args, "session"); if (ref.kind !== "session") throw new Error("send requires session/"); const sessionArgs = ["send", ref.name, ...stripLeadingResource(args, ref.name)]; const result = await runAgentRunRestCommand(config, "sessions", sessionArgs); return renderMutationSummary(command, result, options, options.dryRun ? "Session send plan" : "Session send submitted"); } export function renderedCliResult(ok: boolean, command: string, renderedText: string, contentType: RenderedCliResult["contentType"] = "text/plain"): RenderedCliResult { return { ok, command, renderedText, contentType }; } export function renderResourceResult(command: string, raw: Record, options: AgentRunResourceOptions, kindLabel: string, items: Record[]): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); const payload = { kind: `${kindLabel}List`, count: items.length, items }; if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output); if (options.output === "name") return renderedCliResult(raw.ok !== false, command, items.map((item) => `${String(item.kind ?? kindLabel).toLowerCase()}/${String(item.name ?? "")}`).join("\n")); if (items.length === 0) return renderedCliResult(raw.ok !== false, command, `No ${kindLabel.toLowerCase()} resources found.`); return renderedCliResult(raw.ok !== false, command, renderResourceTable(items, options.output === "wide")); } export function renderQueueAttemptList(command: string, raw: Record, options: AgentRunResourceOptions, requestedTaskId: string, items: Record[]): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); const data = record(innerData(raw)); const taskId = stringOrNull(data.taskId) ?? requestedTaskId; const cursor = typeof data.cursor === "number" ? String(data.cursor) : stringOrNull(data.cursor); const rawItems = arrayRecords(data.items); if (options.full) { return renderMachine(command, { kind: "AttemptList", taskId, count: data.count ?? rawItems.length, cursor, items: rawItems, }, options.output === "yaml" ? "yaml" : "json", raw.ok !== false); } const payload = { kind: "AttemptList", taskId, count: items.length, cursor, items }; if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, raw.ok !== false); if (options.output === "name") return renderedCliResult(raw.ok !== false, command, items.map((item) => `attempt/${String(item.name ?? "")}`).join("\n")); if (items.length === 0) return renderedCliResult(raw.ok !== false, command, `No attempt resources found for task/${taskId}.`); const lines = [ `Attempts for task/${taskId}`, renderTable(["NAME", "INDEX", "STATE", "PREVIOUS", "RUN", "CMD", "RJOB", "AGE", "FAILURE", "REASON"], items.map((item) => [ resourceName(item), displayValue(item.retryIndex), displayValue(item.state), shortId(stringOrDash(item.previousAttemptId)), shortId(stringOrDash(item.runId)), shortId(stringOrDash(item.commandId)), shortId(stringOrDash(item.runnerJobId)), displayValue(item.age), queueAttemptFailureSummary(item), truncateOneLine(displayValue(item.reason), 48), ])), ]; if (cursor !== null) lines.push(`Next: ${nextQueueAttemptPageCommand(command, cursor, options.limit)}`); return renderedCliResult(raw.ok !== false, command, lines.join("\n")); } export function renderQueueRetrySummary(command: string, raw: Record, options: AgentRunResourceOptions, requestedTaskId: string): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); const data = record(innerData(raw)); const task = record(data.task); const attempt = record(data.attempt); const previousAttempt = record(data.previousAttempt); const allowedTransition = record(data.allowedTransition); const taskId = stringOrNull(task.id) ?? requestedTaskId; if (options.full) { return renderMachine(command, { kind: "TaskRetry", taskId, ...data }, options.output === "yaml" ? "yaml" : "json", raw.ok !== false); } const normalizedAttempt = compactQueueRetryAttempt(attempt); const normalizedPreviousAttempt = compactQueueRetryAttempt(previousAttempt); const run = record(data.run); const runId = stringOrNull(run.id) ?? stringOrNull(attempt.runId); const commandResource = record(data.command); const commandId = stringOrNull(commandResource.id) ?? stringOrNull(attempt.commandId); const runnerJob = record(data.runnerJob); const runnerJobId = stringOrNull(runnerJob.id) ?? stringOrNull(attempt.runnerJobId); const payload = { kind: "TaskRetry", taskId, dryRun: options.dryRun, mutation: data.mutation === true, idempotentReplay: data.idempotentReplay === true, task: pickCompact(task, ["id", "state", "queue", "lane", "payloadHash", "updatedAt"]), attempt: normalizedAttempt, previousAttempt: normalizedPreviousAttempt, allowedTransition, run: compactQueueRetryIdentity(run, ["id", "status", "terminalStatus", "createdAt", "updatedAt"]), command: compactQueueRetryIdentity(commandResource, ["id", "type", "state", "terminalStatus", "createdAt", "updatedAt"]), runnerJob: compactQueueRetryIdentity(runnerJob, ["id", "state", "phase", "terminalStatus", "attemptId", "createdAt", "updatedAt"]), }; if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, raw.ok !== false); const retryIndex = attempt.retryIndex ?? allowedTransition.retryIndex; const attemptId = stringOrNull(attempt.attemptId); const previousAttemptId = stringOrNull(previousAttempt.attemptId) ?? stringOrNull(allowedTransition.previousAttemptId); const lines = [ `${options.dryRun ? "Task retry validated" : "Task retry submitted"}: task/${taskId}`, `OK: ${String(raw.ok !== false)}`, `DryRun: ${String(options.dryRun)}`, `Mutation: ${String(data.mutation === true)}`, `IdempotentReplay: ${String(data.idempotentReplay === true)}`, `Transition: ${displayValue(allowedTransition.from)} -> ${displayValue(allowedTransition.to)} retryIndex=${displayValue(retryIndex)}`, `Attempt: ${attemptId === null ? "planned" : `attempt/${shortId(attemptId)}`} state=${displayValue(attempt.state ?? allowedTransition.to)}`, `PreviousAttempt: ${previousAttemptId === null ? "-" : `attempt/${shortId(previousAttemptId)}`}`, ]; if (runId !== null) lines.push(`Run: run/${shortId(runId)}`); if (commandId !== null) lines.push(`Command: command/${shortId(commandId)}`); if (runnerJobId !== null) lines.push(`RunnerJob: runnerjob/${shortId(runnerJobId)}`); const next = [ `bun scripts/cli.ts agentrun get attempts --task ${taskId}`, `bun scripts/cli.ts agentrun describe task/${taskId}`, runId === null ? null : `bun scripts/cli.ts agentrun events run/${runId} --after-seq 0 --limit 100`, runId === null || commandId === null ? null : `bun scripts/cli.ts agentrun describe command/${commandId} --run ${runId}`, ].filter((value): value is string => value !== null); lines.push("", "Next:", ...next.map((value) => ` ${value}`)); return renderedCliResult(raw.ok !== false, command, lines.join("\n")); } function compactQueueRetryAttempt(value: Record): Record | null { if (Object.keys(value).length === 0) return null; const attempt = pickCompact(value, ["attemptId", "taskId", "retryIndex", "state", "idempotencyKey", "reason", "payloadHash", "previousAttemptId", "runId", "commandId", "runnerJobId", "sessionId", "sessionPath", "failureKind", "failureMessage", "createdAt", "updatedAt", "activatedAt", "terminalAt"]); attempt.reason = boundedQueueAttemptText(attempt.reason, 240); attempt.failureMessage = boundedQueueAttemptText(attempt.failureMessage, 320); attempt.sessionPath = boundedQueueAttemptText(attempt.sessionPath, 240); return attempt; } function compactQueueRetryIdentity(value: Record, keys: string[]): Record | null { return Object.keys(value).length === 0 ? null : pickCompact(value, keys); } function nextQueueAttemptPageCommand(command: string, cursor: string, limit: number): string { const base = command .replace(/\s+--cursor(?:=|\s+)\S+/gu, "") .replace(/\s+--limit(?:=|\s+)\S+/gu, "") .trim(); return `bun scripts/cli.ts ${base} --cursor ${cursor} --limit ${limit}`; } export function renderDescribe(command: string, raw: Record, options: AgentRunResourceOptions, ref: AgentRunResourceRef, text: string): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); if (options.output === "json" || options.output === "yaml") { const resource = record(innerData(raw)); const visibleResource = ref.kind === "runnerjob" && !options.full ? compactRunnerJobDescriptionPayload(resource) : resource; return renderMachine(command, { kind: ref.kind, name: ref.name, resource: visibleResource }, options.output, raw.ok !== false); } return renderedCliResult(raw.ok !== false, command, text); } export function compactRunnerJobDescriptionPayload(value: Record): Record { const observation = record(value.runtimeObservation); const runner = record(observation.runner); const target = record(observation.target); const runId = value.runId ?? runner.runId ?? null; const commandId = value.commandId ?? runner.commandId ?? null; const runnerJobId = value.id ?? runner.runnerJobId ?? null; const targetArgs = typeof target.node === "string" && typeof target.lane === "string" ? ` --node ${target.node} --lane ${target.lane}` : ""; return { id: runnerJobId, runId, commandId, attemptId: value.attemptId ?? null, runnerId: value.runnerId ?? runner.runnerId ?? null, namespace: value.namespace ?? runner.namespace ?? target.namespace ?? null, jobName: value.jobName ?? runner.name ?? null, state: value.state ?? null, managerPhase: value.managerPhase ?? value.phase ?? null, terminalStatus: value.terminalStatus ?? null, failureKind: value.failureKind ?? null, image: value.image ?? null, createdAt: value.createdAt ?? null, startedAt: value.startedAt ?? null, finishedAt: value.finishedAt ?? null, runtimeObservation: { ok: observation.ok ?? false, reason: observation.reason ?? null, observedAt: observation.observedAt ?? null, target, managerFacts: observation.managerFacts ?? null, kubernetesFacts: observation.kubernetesFacts ?? null, runner: Object.keys(runner).length === 0 ? null : { name: runner.name ?? null, runId: runner.runId ?? null, commandId: runner.commandId ?? null, runnerJobId: runner.runnerJobId ?? null, runnerId: runner.runnerId ?? null, createdAt: runner.createdAt ?? null, lastActiveAt: runner.lastActiveAt ?? null, lastActiveAgeMs: runner.lastActiveAgeMs ?? null, runStatus: runner.runStatus ?? null, runClaimedBy: runner.runClaimedBy ?? null, commandState: runner.commandState ?? null, heartbeatAt: runner.heartbeatAt ?? null, heartbeatAgeMs: runner.heartbeatAgeMs ?? null, state: runner.state ?? null, jobStatus: runner.jobStatus ?? null, podPhases: Array.isArray(runner.podPhases) ? runner.podPhases.slice(0, 4) : [], waitingReasons: Array.isArray(runner.waitingReasons) ? runner.waitingReasons.slice(0, 8) : [], podObservations: Array.isArray(runner.podObservations) ? runner.podObservations.slice(0, 2) : [], identityComplete: runner.identityComplete === true, managerFactsComplete: runner.managerFactsComplete === true, podFactsComplete: runner.podFactsComplete === true, cleanupAuthority: runner.cleanupAuthority ?? null, cleanupDecisionComputed: runner.cleanupDecisionComputed === true, safeCleanupCandidate: typeof runner.safeCleanupCandidate === "boolean" ? runner.safeCleanupCandidate : null, protectedActive: typeof runner.protectedActive === "boolean" ? runner.protectedActive : null, protectedReason: runner.protectedReason ?? null, candidateKind: runner.candidateKind ?? null, classification: runner.classification ?? null, valuesPrinted: false, }, mutation: false, valuesPrinted: false, }, next: { events: typeof runId === "string" ? `bun scripts/cli.ts agentrun events run/${runId} --after-seq 0 --limit 100${targetArgs}` : null, command: typeof runId === "string" && typeof commandId === "string" ? `bun scripts/cli.ts agentrun describe command/${commandId} --run ${runId}${targetArgs}` : null, full: typeof runId === "string" && typeof runnerJobId === "string" ? `bun scripts/cli.ts agentrun describe runnerjob/${runnerJobId} --run ${runId}${targetArgs} --full -o json` : null, }, valuesPrinted: false, }; } export interface EventLikePageDisclosure { requestedLimit: number; effectiveLimit: number; fetchedCount: number; hasMore: boolean; nextAfterSeq: number | string | null; detailCommand: string; } export function renderEventLike(command: string, raw: Record, options: AgentRunResourceOptions, kind: string, items: Record[], sourceName: string, pageDisclosure: EventLikePageDisclosure | null = null): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); const data = record(innerData(raw)); const nextAfterSeq = pageDisclosure?.nextAfterSeq ?? data.nextAfterSeq ?? null; const payload = { kind: `${kind}List`, source: sourceName, nextAfterSeq, count: items.length, ...(pageDisclosure === null ? {} : { requestedLimit: pageDisclosure.requestedLimit, effectiveLimit: pageDisclosure.effectiveLimit, fetchedCount: pageDisclosure.fetchedCount, hasMore: pageDisclosure.hasMore, disclosure: { output: "omitted; metadata only", identity: "item.identity; detail --detail-seq item.seq", detail: pageDisclosure.detailCommand, full: "--full", raw: "--raw", }, }), items, }; if (options.output === "json" && pageDisclosure !== null) { return renderedCliResult(raw.ok !== false, command, `${JSON.stringify(payload)}\n`, "application/json"); } if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, raw.ok !== false); const lines = pageDisclosure === null ? [ `${kind}s for ${sourceName}`, renderTable(["SEQ", "TYPE", "STATUS", "COMMAND", "SUMMARY"], items.map((item) => [ String(item.seq ?? "-"), String(item.type ?? "-"), String(item.status ?? item.phase ?? "-"), shortId(String(item.commandId ?? "-")), truncateOneLine(String(item.summary ?? item.text ?? "") || "-", 96), ])), ] : [ `${kind}s for ${sourceName}`, renderTable(["SEQ", "TYPE", "STATUS", "COMMAND", "TOOL / DETAIL", "EXIT", "DURATION", "OUTPUT"], items.map((item) => [ String(item.seq ?? "-"), String(item.type ?? "-"), String(item.status ?? item.phase ?? "-"), shortId(String(item.commandId ?? "-")), eventHumanDetail(item), displayValue(item.exitCode), eventDurationCell(item.durationMs), eventOutputCell(item), ])), ]; if (pageDisclosure !== null) lines.push(`Detail: ${pageDisclosure.detailCommand}`); if (nextAfterSeq !== undefined && nextAfterSeq !== null) lines.push(`Next: bun scripts/cli.ts ${nextPagedResourceCommand(command, String(nextAfterSeq), options.limit)}`); return renderedCliResult(raw.ok !== false, command, lines.join("\n")); } function eventHumanDetail(item: Record): string { const tool = stringOrNull(item.tool); const command = stringOrNull(item.command); const summary = stringOrNull(item.summary); const detail = command === null ? summary ?? tool ?? "-" : tool === null ? command : `${tool} ${command}`; return truncateOneLine(detail, 72); } function eventDurationCell(value: unknown): string { return typeof value === "number" && Number.isFinite(value) ? `${value}ms` : "-"; } function eventOutputCell(item: Record): string { const bytes = typeof item.outputBytes === "number" && Number.isFinite(item.outputBytes) ? `${item.outputBytes}B` : null; const fingerprint = stringOrNull(item.outputSha256); const shortFingerprint = fingerprint === null ? null : fingerprint.length <= 24 ? fingerprint : `${fingerprint.slice(0, 21)}...`; const truncated = item.outputTruncated === true ? "truncated" : null; const omitted = item.outputOmitted === true ? "omitted" : null; return [bytes, shortFingerprint, truncated, omitted].filter((value): value is string => value !== null).join(" ") || "-"; } export function renderResultSummary(command: string, raw: Record, options: AgentRunResourceOptions, ref: AgentRunResourceRef): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); const data = record(innerData(raw)); if (options.output === "json" || options.output === "yaml") return renderMachine(command, { kind: "Result", ref, result: data }, options.output, raw.ok !== false); const executionOk = typeof data.executionOk === "boolean" ? data.executionOk : typeof data.ok === "boolean" ? data.ok : raw.ok !== false; const lines = [ `Result: ${ref.kind}/${shortId(ref.name)}`, `State: ${displayValue(data.state ?? data.status ?? data.terminalStatus ?? "-")}`, `OK: ${String(executionOk)}`, ]; const providerTerminalFailure = typeof data.providerTerminalFailure === "boolean" ? data.providerTerminalFailure : null; const authority = stringOrNull(data.finalResponseAuthority); const fallback = typeof data.finalResponseFallback === "boolean" ? data.finalResponseFallback : null; const needsContinuation = typeof data.needsContinuation === "boolean" ? data.needsContinuation : null; if (providerTerminalFailure !== null) lines.push(`ProviderTerminalFailure: ${String(providerTerminalFailure)}`); if (authority !== null) lines.push(`Authority: ${authority}`); if (fallback !== null) lines.push(`Fallback: ${String(fallback)}`); if (needsContinuation !== null) lines.push(`NeedsContinuation: ${String(needsContinuation)}`); const finalRecord = record(data.finalResponse); const final = stringOrNull(data.finalResponse) ?? stringOrNull(finalRecord.content) ?? stringOrNull(finalRecord.text) ?? stringOrNull(finalRecord.message) ?? stringOrNull(data.output) ?? stringOrNull(data.summary) ?? stringOrNull(data.result); if (final !== null) lines.push("", truncateMultiline(final, options.fullText ? 8000 : 1600)); 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")); } export function renderMutationSummary(command: string, raw: Record, options: AgentRunResourceOptions, headline: string, overrideNextLines?: string[]): RenderedCliResult { if (options.raw) return renderMachine(command, raw, "json", raw.ok !== false); if (options.output === "json" || options.output === "yaml") return renderMachine(command, raw, options.output, raw.ok !== false); const data = record(innerData(raw)); const id = stringOrNull(data.id) ?? stringOrNull(data.taskId) ?? stringOrNull(data.sessionId); const lines = [ headline, `OK: ${String(raw.ok !== false)}`, ]; if (id !== null) lines.push(`Name: ${id}`); const decision = stringOrNull(data.decision); const internalCommandType = stringOrNull(data.internalCommandType); const dryRun = data.dryRun !== undefined ? data.dryRun : raw.dryRun; const mutation = data.mutation !== undefined ? data.mutation : raw.mutation; if (dryRun !== undefined) lines.push(`DryRun: ${String(dryRun)}`); if (mutation !== undefined) lines.push(`Mutation: ${String(mutation)}`); if (decision !== null) lines.push(`Decision: ${decision}`); if (internalCommandType !== null) lines.push(`InternalCommandType: ${internalCommandType}`); lines.push(...renderCancelLifecycleMutationLines(record(data.cancelLifecycle ?? raw.cancelLifecycle))); const next = record(raw.next ?? data.next); const nextLines = (overrideNextLines ?? Object.values(next).map(String)).filter((line) => line.length > 0).slice(0, 5); if (nextLines.length > 0) lines.push("", "Next:", ...nextLines.map((line) => ` ${line}`)); return renderedCliResult(raw.ok !== false, command, lines.join("\n")); } export function renderCancelLifecycleMutationLines(lifecycle: Record): string[] { if (Object.keys(lifecycle).length === 0) return []; const authority = record(lifecycle.authority); const runnerAbort = record(lifecycle.runnerAbort); const fencing = record(lifecycle.fencing); const expectedStages = Array.isArray(lifecycle.expectedStages) ? lifecycle.expectedStages.map(String).filter((value) => value.length > 0) : []; const node = stringOrNull(authority.node); const lane = stringOrNull(authority.lane); const target = node !== null && lane !== null ? `${node}/${lane}` : "-"; const lines = ["", "CancelLifecycle:"]; lines.push(` Authority: ${displayValue(authority.transport)} policy=${displayValue(authority.policySource)} lane=${target}`); const namespace = stringOrNull(authority.namespace); const deployment = stringOrNull(authority.managerDeployment); if (namespace !== null || deployment !== null) lines.push(` Runtime: ns=${displayValue(namespace)} manager=${displayValue(deployment)}`); const cascadeScope = stringOrNull(lifecycle.cascadeScope); if (cascadeScope !== null) lines.push(` Cascade: ${cascadeScope}`); if (Object.keys(runnerAbort).length > 0) { lines.push(` RunnerAbort: mode=${displayValue(runnerAbort.deliveryMode)} gracefulMs=${displayValue(runnerAbort.gracefulAbortMs)} killMs=${displayValue(runnerAbort.killEscalationMs)}`); } if (Object.keys(fencing).length > 0) { lines.push(` Fencing: cancelEpoch=${displayValue(fencing.cancelEpoch)} staleHeartbeatMs=${displayValue(fencing.staleHeartbeatFencingMs)} lateWrite=${displayValue(fencing.lateWriteFencing)}`); } if (expectedStages.length > 0) lines.push(` Stages: ${expectedStages.join(", ")}`); const terminalAuthority = stringOrNull(lifecycle.terminalAuthority); if (terminalAuthority !== null) lines.push(` Terminal: ${terminalAuthority}`); return lines; } export function rerunWithoutDryRun(command: string): string { return `bun scripts/cli.ts ${command.replace(/\s+--dry-run\b/gu, "").trim()}`; } export function renderMachine(command: string, value: unknown, mode: "json" | "yaml", ok = true): RenderedCliResult { const text = mode === "json" ? `${JSON.stringify(value, null, 2)}\n` : `${Bun.YAML.stringify(value)}\n`; return renderedCliResult(ok, command, text, mode === "json" ? "application/json" : "application/yaml"); } export function parseResourceKind(raw: string | undefined): AgentRunResourceKind | null { if (raw === undefined) return null; const value = raw.toLowerCase().replace(/s$/u, ""); if (value === "task" || value === "qt" || value === "queue" || value === "queuetask") return "task"; if (value === "attempt") return "attempt"; if (value === "run") return "run"; if (value === "command" || value === "cmd") return "command"; if (value === "runnerjob" || value === "runner-job" || value === "rjob" || value === "job") return "runnerjob"; if (value === "session" || value === "ses") return "session"; if (value === "aipodspec" || value === "aipod-spec" || value === "aipod-specs" || value === "aps") return "aipodspec"; return null; } export function parseResourceRef(action: string | undefined, args: string[], defaultKind: AgentRunResourceKind | null = null): AgentRunResourceRef { const raw = action ?? ""; if (raw.includes("/")) { const [kindRaw, ...nameParts] = raw.split("/"); const kind = parseResourceKind(kindRaw); const name = nameParts.join("/"); if (kind === null || name.length === 0) throw new Error(`invalid resource reference: ${raw}`); return { kind, name }; } const kind = parseResourceKind(raw); if (kind !== null) { const name = args.find((arg) => !arg.startsWith("-") && arg !== raw); if (name === undefined) throw new Error(`${kind} reference requires a name`); return { kind, name }; } if (defaultKind !== null && raw.length > 0) return { kind: defaultKind, name: raw }; throw new Error("resource reference required, for example task/ or session/"); } export function stripLeadingResource(args: string[], resourceName: string): string[] { let skipped = false; const result: string[] = []; for (const arg of args) { if (!skipped && (arg === resourceName || arg.endsWith(`/${resourceName}`) || parseResourceKind(arg) !== null)) { skipped = true; continue; } result.push(arg); } return result; } export function requiredContext(context: string, flag: string): never { throw new Error(`${context} requires ${flag}`); } export function innerData(raw: Record): unknown { const data = raw.data; if (typeof data === "object" && data !== null && "data" in data && Object.keys(raw).length <= 3) return record(data).data; return data ?? raw; } export function normalizeTaskItems(data: unknown, options: AgentRunResourceOptions): Record[] { const items = arrayRecords(record(data).items ?? data); return items .filter((item) => options.queue === null || String(item.queue ?? "") === options.queue) .filter((item) => options.state === null || options.state.split(",").includes(String(item.state ?? ""))) .filter((item) => !options.unread || item.unread === true || item.attention === true) .map((item) => { const attempt = record(item.latestAttempt); return { kind: "task", name: item.id, state: item.state, queue: item.queue, lane: item.lane, profile: item.backendProfile, run: attempt.runId, command: attempt.commandId, runnerjob: attempt.runnerJobId, session: attempt.sessionId ?? stringOrNull(record(item.sessionRef).sessionId), age: relativeAge(stringOrNull(item.updatedAt) ?? stringOrNull(item.createdAt)), title: item.title, updatedAt: item.updatedAt, attention: item.unread === true || item.attention === true ? "unread" : "", }; }); } export function normalizeQueueAttemptItems(data: unknown): Record[] { const items = arrayRecords(record(data).items ?? data); return items.map((item) => ({ kind: "attempt", name: item.attemptId, attemptId: item.attemptId, taskId: item.taskId, retryIndex: item.retryIndex, state: item.state, idempotencyKey: item.idempotencyKey, reason: boundedQueueAttemptText(item.reason, 240), payloadHash: item.payloadHash, previousAttemptId: item.previousAttemptId, runId: item.runId, commandId: item.commandId, runnerJobId: item.runnerJobId, sessionId: item.sessionId, sessionPath: boundedQueueAttemptText(item.sessionPath, 240), failureKind: item.failureKind, failureMessage: boundedQueueAttemptText(item.failureMessage, 320), createdAt: item.createdAt, updatedAt: item.updatedAt, activatedAt: item.activatedAt, terminalAt: item.terminalAt, age: relativeAge(stringOrNull(item.updatedAt) ?? stringOrNull(item.createdAt)), })); } function boundedQueueAttemptText(value: unknown, maxChars: number): string | null { const text = stringOrNull(value); return text === null ? null : truncateOneLine(text, maxChars); } function queueAttemptFailureSummary(item: Record): string { const kind = stringOrNull(item.failureKind); const message = stringOrNull(item.failureMessage); if (kind === null && message === null) return "-"; return truncateOneLine(kind === null ? message ?? "-" : message === null ? kind : `${kind}: ${message}`, 72); } export function taskListState(options: AgentRunResourceOptions): string { const requested = options.state?.split(",").map((item) => item.trim()).filter((item) => item.length > 0); return requested?.[0] ?? "running"; } export function normalizeSessionItems(data: unknown): Record[] { const items = arrayRecords(record(data).items ?? data); return items.map((item) => ({ kind: "session", name: item.id ?? item.sessionId ?? item.name, state: item.state ?? item.status ?? item.phase ?? "-", queue: item.queue ?? "", lane: item.lane ?? "", profile: item.backendProfile ?? item.profile ?? "", run: item.runId ?? "", command: item.commandId ?? "", runnerjob: item.runnerJobId ?? "", session: item.id ?? item.sessionId ?? item.name, age: relativeAge(stringOrNull(item.updatedAt) ?? stringOrNull(item.createdAt)), title: item.title ?? "", })); } export function normalizeAipodSpecItems(data: unknown): Record[] { const items = arrayRecords(record(data).items ?? data); return items.map((item) => ({ kind: "aipodspec", name: item.name ?? item.id ?? item.metadata?.toString(), state: item.state ?? "available", queue: "", lane: item.lane ?? "", profile: item.backendProfile ?? item.profile ?? "", title: item.description ?? item.title ?? "", })); } export function normalizeAttemptResources(task: Record, kind: "run" | "command" | "runnerjob"): Record[] { const attempt = record(task.latestAttempt); const key = kind === "run" ? "runId" : kind === "command" ? "commandId" : "runnerJobId"; const name = stringOrNull(attempt[key]); if (name === null) return []; return [{ kind, name, state: attempt.state ?? task.state, queue: task.queue, lane: task.lane, run: attempt.runId, command: attempt.commandId, runnerjob: attempt.runnerJobId, session: attempt.sessionId, title: task.title, }]; } export function normalizeSingleCommand(data: unknown): Record[] { const item = record(data); return [{ kind: "command", name: item.id ?? item.commandId ?? "", state: item.state ?? item.status ?? "", run: item.runId ?? "", command: item.id ?? item.commandId ?? "", session: item.sessionId ?? "", title: item.summary ?? item.commandType ?? "", }]; } export function normalizeRunnerJobItems(data: unknown): Record[] { const items = arrayRecords(record(data).items ?? data); return items.map((item) => ({ kind: "runnerjob", name: item.id ?? item.runnerJobId ?? item.name ?? item.jobName, state: item.phase ?? item.state ?? item.status ?? "", run: item.runId ?? "", command: item.commandId ?? "", runnerjob: item.id ?? item.runnerJobId ?? item.name ?? item.jobName, session: item.sessionId ?? "", title: item.image ?? item.namespace ?? "", })); } const EVENT_SUMMARY_CHARS = 160; const EVENT_COMMAND_CHARS = 160; export function normalizeEventItems(data: unknown, runId: string | null = null): Record[] { return arrayRecords(record(data).items ?? data).map((item) => { const payload = record(item.payload); const seq = finiteNumberOrNull(item.seq); const eventId = boundedEventIdentity(item.id ?? item.eventId); const tool = agentRunEventTool(payload); const eventCommand = eventCommandFirstLine(payload); const exitCode = finiteNumberOrNull(payload.exitCode); const durationMs = finiteNumberOrNull(payload.durationMs); const output = agentRunEventOutputMetadata(String(item.type ?? ""), payload); const identity = eventId === null ? runId === null || seq === null ? null : `run/${runId}/seq/${seq}` : `event/${eventId}`; return { seq: seq ?? item.seq, type: boundedEventScalar(item.type, 64), status: agentRunEventStatus(item, payload), ...(item.phase === undefined && payload.phase === undefined ? {} : { phase: boundedEventScalar(item.phase ?? payload.phase, 80) }), commandId: agentRunEventCommandId(item, payload), ...(identity === null ? {} : { identity }), ...(tool === null ? {} : { tool }), ...(eventCommand === null ? {} : { command: eventCommand }), ...(exitCode === null ? {} : { exitCode }), ...(durationMs === null ? {} : { durationMs }), ...(output.bytes === null ? {} : { outputBytes: output.bytes }), ...(output.sha256 === null ? {} : { outputSha256: output.sha256 }), ...(output.truncated === null ? {} : { outputTruncated: output.truncated }), ...(output.omitted ? { outputOmitted: true } : {}), summary: agentRunEventSummary(item, payload), }; }); } export function normalizeLogItems(data: unknown): Record[] { return arrayRecords(record(data).items ?? data).map((item) => { const payload = record(item.payload); return { seq: item.seq, type: item.type ?? item.role ?? "output", status: agentRunEventStatus(item, payload), commandId: agentRunEventCommandId(item, payload), summary: agentRunLogSummary(item, payload), }; }); } function agentRunLogSummary(item: Record, payload: Record): string { const type = stringOrNull(item.type); const parts: string[] = []; const push = (value: unknown): void => { const text = eventText(value); if (text !== null && !parts.includes(text)) parts.push(text); }; if (type === "error") { push(payload.failureKind); const error = record(payload.error); push(error.message); push(error.additionalDetails); if (payload.willRetry === true) push("willRetry=true"); } if (type === "backend_status") { push(payload.phase); push(formatCountField("tools", record(payload.tools).count)); push(formatCountField("bundles", record(payload.bundles).count)); push(formatCountField("promptRefs", record(payload.promptRefs).count)); push(payload.jobName); push(payload.namespace); } if (type === "tool_call") { push(payload.summary); push(payload.toolName); push(payload.command); const exitCode = payload.exitCode; if (exitCode !== undefined && exitCode !== null) push(`exit=${String(exitCode)}`); push(payload.outputSummary); } if (type === "command_output") push(commandOutputSummary(payload)); if (type === "assistant_message") { push(payload.summary); push(payload.text); } push(item.summary); push(item.outputSummary); push(item.text); if (type !== "command_output") { push(payload.summary); push(payload.outputSummary); push(payload.text); } push(payload.message); push(payload.reason); return parts.join("; "); } export function agentRunEventCommandId(item: Record, payload: Record): string { return boundedEventIdentity(stringOrNull(item.commandId) ?? stringOrNull(payload.commandId) ?? stringOrNull(record(payload.summary).commandId) ?? "-") ?? "-"; } export function agentRunEventStatus(item: Record, payload: Record): string { return boundedEventScalar(stringOrNull(item.status) ?? stringOrNull(item.phase) ?? stringOrNull(payload.status) ?? stringOrNull(payload.phase) ?? stringOrNull(payload.failureKind) ?? (item.type === "error" ? "error" : ""), 80); } export function agentRunEventSummary(item: Record, payload: Record): string { const type = stringOrNull(item.type); if (type === "tool_call") return toolCallEventSummary(item, payload); if (type === "command_output") return commandOutputEventSummary(payload); const parts: string[] = []; const push = (value: unknown): void => { const text = eventText(value); const bounded = text === null ? null : truncateOneLine(text, EVENT_SUMMARY_CHARS); if (bounded !== null && !parts.includes(bounded)) parts.push(bounded); }; if (type === "error") { push(payload.failureKind); const error = record(payload.error); push(error.message); push(error.additionalDetails); if (payload.willRetry === true) push("willRetry=true"); } if (type === "backend_status") { push(payload.phase); push(formatCountField("tools", record(payload.tools).count)); push(formatCountField("bundles", record(payload.bundles).count)); push(formatCountField("promptRefs", record(payload.promptRefs).count)); push(payload.jobName); push(payload.namespace); } if (type === "assistant_message") { push(payload.summary); push(payload.text); } push(item.summary); push(item.outputSummary); push(item.text); if (type !== "command_output") { push(payload.summary); push(payload.outputSummary); push(payload.text); } push(payload.message); push(payload.reason); return truncateOneLine(parts.join("; "), EVENT_SUMMARY_CHARS); } function toolCallEventSummary(item: Record, payload: Record): string { const tool = agentRunEventTool(payload); const status = agentRunEventStatus(item, payload); const parts = [tool, status.length === 0 ? null : status] .filter((value): value is string => value !== null); return truncateOneLine(parts.join("; "), EVENT_SUMMARY_CHARS); } function commandOutputEventSummary(payload: Record): string { const stream = boundedEventScalar(payload.stream, 40); const parts = [stream.length === 0 ? "command output" : stream] .filter((value): value is string => value !== null); return truncateOneLine(parts.join("; "), EVENT_SUMMARY_CHARS); } function agentRunEventTool(payload: Record): string | null { const value = stringOrNull(payload.toolName) ?? stringOrNull(payload.type) ?? stringOrNull(payload.name) ?? stringOrNull(payload.method); return value === null ? null : truncateOneLine(value, 80); } function eventCommandFirstLine(payload: Record): string | null { const value = stringOrNull(payload.command) ?? stringOrNull(payload.commandLine); if (value === null) return null; const firstLine = value.split(/\r?\n/u)[0] ?? ""; const bounded = truncateOneLine(firstLine, EVENT_COMMAND_CHARS); return bounded.length === 0 ? null : bounded; } function agentRunEventOutputMetadata(type: string, payload: Record): { bytes: number | null; sha256: string | null; truncated: boolean | null; omitted: boolean } { const summary = record(payload.summary); const material = eventOutputMaterial(type, payload, summary); const lifecycle = [stringOrNull(payload.status), stringOrNull(payload.method), stringOrNull(payload.phase)].filter((value): value is string => value !== null).join(" "); const terminalToolOutputFacts = type === "tool_call" && /(?:completed|failed|succeeded)/iu.test(lifecycle) && firstFiniteNumber(payload.outputBytes, payload.textBytes, summary.outputBytes, summary.textBytes) !== null; const outputPresent = material !== null || type === "command_output" || terminalToolOutputFacts; const bytes = outputPresent ? firstFiniteNumber(payload.outputBytes, payload.textBytes, summary.outputBytes, summary.textBytes) ?? (material === null ? null : Buffer.byteLength(material, "utf8")) : null; const providedHash = outputPresent ? firstEventFingerprint(payload.outputSha256, payload.outputHash, summary.outputSha256, summary.outputHash) : null; const sha256 = providedHash ?? (material === null ? null : sha256Fingerprint(material)); const truncationFacts = [payload.outputTruncated, payload.textTruncated, summary.outputTruncated, summary.textTruncated] .filter((value): value is boolean => typeof value === "boolean"); return { bytes, sha256, truncated: outputPresent && truncationFacts.length > 0 ? truncationFacts.some(Boolean) : null, omitted: outputPresent, }; } function eventOutputMaterial(type: string, payload: Record, summary: Record): string | null { const nestedSummary = stringOrNull(summary.text); const nestedSummaryContainsOutput = nestedSummary !== null && /(?:^|\s)output=/u.test(nestedSummary); const values = type === "command_output" ? [payload.text, payload.output, payload.stdout, payload.stderr, payload.outputSummary, summary.text] : [payload.output, payload.outputSummary, payload.stdout, payload.stderr, payload.stdoutSummary, payload.stderrSummary, nestedSummaryContainsOutput ? nestedSummary : null]; for (const value of values) { const text = stringOrNull(value); if (text !== null) return text; } return null; } function firstFiniteNumber(...values: unknown[]): number | null { for (const value of values) { const number = finiteNumberOrNull(value); if (number !== null) return number; } return null; } function finiteNumberOrNull(value: unknown): number | null { return typeof value === "number" && Number.isFinite(value) ? value : null; } function firstEventFingerprint(...values: unknown[]): string | null { for (const value of values) { const text = stringOrNull(value); if (text === null) continue; if (/^sha256:[a-f0-9]{64}$/iu.test(text)) return text.toLowerCase(); if (/^[a-f0-9]{64}$/iu.test(text)) return `sha256:${text.toLowerCase()}`; } return null; } function boundedEventIdentity(value: unknown): string | null { const text = stringOrNull(value); return text === null ? null : truncateOneLine(text, 240); } function boundedEventScalar(value: unknown, maxChars: number): string { const text = stringOrNull(value); return text === null ? "" : truncateOneLine(text, maxChars); } export function commandOutputSummary(payload: Record): string | null { const raw = commandOutputText(payload); if (raw === null) return null; const structured = parseJsonRecordFromText(raw); if (structured !== null) { const summary = summarizeStructuredCliOutput(structured); if (summary !== null) return summary; } return summarizePartialJsonCommandOutput(raw) ?? summarizeTextCommandOutput(raw); } export function commandOutputText(payload: Record): string | null { const summary = record(payload.summary); const summaryText = stringOrNull(summary.text); const payloadText = stringOrNull(payload.text); if (summary.textTruncated === true && payloadText !== null) return payloadText; return summaryText ?? payloadText ?? stringOrNull(payload.outputSummary) ?? eventText(payload.summary); } export function parseJsonRecordFromText(raw: string): Record | null { const trimmed = raw.trim(); const direct = tryParseJsonRecord(trimmed); if (direct !== null) return direct; const firstBrace = trimmed.indexOf("{"); const lastBrace = trimmed.lastIndexOf("}"); if (firstBrace >= 0 && lastBrace > firstBrace) { const sliced = tryParseJsonRecord(trimmed.slice(firstBrace, lastBrace + 1)); if (sliced !== null) return sliced; } const line = trimmed .split(/\r?\n/u) .map((item) => item.trim()) .reverse() .find((item) => item.startsWith("{") && item.endsWith("}")); return line === undefined ? null : tryParseJsonRecord(line); } export function tryParseJsonRecord(raw: string): Record | null { try { const parsed = JSON.parse(raw) as unknown; return isRecord(parsed) ? parsed : null; } catch { return null; } } export function summarizeStructuredCliOutput(value: Record): string | null { const parts: string[] = []; const action = firstPathText(value, ["action", "operation", "command", "kind"]); const status = firstPathText(value, ["status", "state", "phase", "body.status"]); const failure = firstPathText(value, ["failureKind", "error.failureKind", "body.failureKind"]); const message = firstPathText(value, ["error.message", "error.additionalDetails", "message", "reason", "body.message", "body.error", "result.message"]); const primary = [ action, status, labeledSummaryValue("http", firstPathText(value, ["httpStatus", "request.httpStatus"])), ].filter((item): item is string => item !== null); if (primary.length > 0) pushSummaryPart(parts, primary.join(" ")); pushLabeledSummaryPart(parts, "failure", failure); if (failure !== null || isFailureLikeStatus(status)) pushLabeledSummaryPart(parts, "message", message); pushLabeledSummaryPart(parts, "trace", firstPathText(value, ["traceId", "lastTraceId", "trace.traceId", "trace.id", "body.traceId", "body.trace.id", "providerTrace.traceId", "runnerTrace.traceId"]), true); pushLabeledSummaryPart(parts, "session", firstPathText(value, ["sessionId", "session.sessionId", "body.sessionId", "workspace.selectedAgentSessionId", "workspace.selectedConversation.sessionId"]), true); pushLabeledSummaryPart(parts, "conversation", firstPathText(value, ["conversationId", "session.conversationId", "body.conversationId", "workspace.selectedConversationId", "workspace.selectedConversation.conversationId"]), true); pushLabeledSummaryPart(parts, "thread", firstPathText(value, ["threadId", "session.threadId", "body.threadId", "workspace.selectedConversation.threadId"]), true); pushLabeledSummaryPart(parts, "provider", firstPathText(value, ["providerProfile", "backendProfile", "profile", "session.providerProfile", "workspace.providerProfile"])); pushLabeledSummaryPart(parts, "route", commandOutputRouteSummary(value)); pushLabeledSummaryPart(parts, "pipeline", firstPathText(value, ["pipelineRun", "pipelineRunName", "pipelineRun.name", "pipeline.runName", "pipeline.name"]), true); pushLabeledSummaryPart(parts, "run", firstPathText(value, ["runId", "agentRun.runId"]), true); pushLabeledSummaryPart(parts, "cmd", firstPathText(value, ["commandId", "agentRun.commandId", "providerTrace.commandId", "runnerTrace.commandId"]), true); pushLabeledSummaryPart(parts, "job", firstPathText(value, ["jobName", "agentRun.jobName", "runnerJobId"]), true); pushLabeledSummaryPart(parts, "lane", firstPathText(value, ["runtimeEndpoint.lane", "lane"])); pushLabeledSummaryPart(parts, "ns", firstPathText(value, ["runtimeEndpoint.namespace", "namespace"])); pushLabeledSummaryPart(parts, "actor", firstPathText(value, ["actor.username", "body.actor.username", "actor.displayName", "body.actor.displayName"])); pushLabeledSummaryPart(parts, "auth", firstPathText(value, ["authMethod", "auth.authMethod", "body.authMethod"])); pushLabeledSummaryPart(parts, "reply", firstPathText(value, ["reply.content", "result.reply.content", "body.reply.content", "answer", "content", "body.content"])); if (failure === null && !isFailureLikeStatus(status)) pushLabeledSummaryPart(parts, "message", message); const itemsCount = arrayRecords(value.items).length; if (itemsCount > 0) pushSummaryPart(parts, `items=${itemsCount}`); const eventsCount = arrayRecords(value.events).length; if (eventsCount > 0) pushSummaryPart(parts, `events=${eventsCount}`); if (value.sessionUsable === false) pushSummaryPart(parts, "sessionUsable=false"); if (record(value.summary).textTruncated === true || value.textTruncated === true || value.outputTruncated === true) pushSummaryPart(parts, "truncated=true"); return parts.length > 0 ? parts.slice(0, 10).join("; ") : null; } export function commandOutputRouteSummary(value: Record): string | null { const route = record(value.route); const request = record(value.request); const method = stringOrNull(route.method) ?? stringOrNull(request.method); const path = stringOrNull(route.path) ?? stringOrNull(request.path); if (method !== null && path !== null) return `${method} ${path}`; return stringOrNull(request.url) ?? stringOrNull(value.baseUrl); } export function firstPathText(value: Record, paths: string[]): string | null { for (const path of paths) { const text = summaryScalar(pathValue(value, path)); if (text !== null) return text; } return null; } export function pathValue(value: Record, path: string): unknown { let current: unknown = value; for (const key of path.split(".")) { if (!isRecord(current)) return undefined; current = current[key]; } return current; } export function summaryScalar(value: unknown): string | null { if (typeof value === "string") { const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } if (typeof value === "number" && Number.isFinite(value)) return String(value); if (typeof value === "boolean") return String(value); return null; } export function isFailureLikeStatus(value: string | null): boolean { return value !== null && /(?:fail|error|timeout|cancel|interrupt|denied|rejected)/iu.test(value); } export function pushLabeledSummaryPart(parts: string[], label: string, value: string | null, shorten = false): void { const text = labeledSummaryValue(label, value, shorten); if (text !== null) pushSummaryPart(parts, text); } export function labeledSummaryValue(label: string, value: string | null, shorten = false): string | null { if (value === null) return null; const rendered = shorten ? shortId(value) : truncateOneLine(value, 80); return `${label}=${rendered}`; } export function pushSummaryPart(parts: string[], value: string): void { const text = truncateOneLine(value, 160); if (text.length > 0 && !parts.includes(text)) parts.push(text); } export function summarizeTextCommandOutput(raw: string): string | null { const lines = raw .split(/\r?\n/u) .map((line) => line.trim()) .filter((line) => line.length > 0 && !isLowSignalCommandOutputLine(line)); const signal = lines.find((line) => /(?:trace|session|conversation|thread|status|failure|error|route|http|pipeline|provider|commit|succeeded|failed|created)/iu.test(line)) ?? lines[0] ?? null; if (signal === null) return null; return truncateOneLine(cleanCommandOutputLine(signal), 220); } export function summarizePartialJsonCommandOutput(raw: string): string | null { const fields = new Map(); for (const line of raw.split(/\r?\n/u)) { const field = jsonLineField(line); if (field === null || isLowSignalJsonField(field.key)) continue; if (!fields.has(field.key) && field.value.length > 0) fields.set(field.key, field.value); } const parts: string[] = []; const action = fields.get("action") ?? fields.get("operation") ?? fields.get("command") ?? fields.get("kind") ?? null; const status = fields.get("status") ?? fields.get("state") ?? fields.get("phase") ?? null; const http = fields.get("httpStatus") ?? null; const failure = fields.get("failureKind") ?? null; const message = fields.get("message") ?? fields.get("reason") ?? fields.get("error") ?? null; const primary = [action, status, labeledSummaryValue("http", http)].filter((item): item is string => item !== null); if (primary.length > 0) pushSummaryPart(parts, primary.join(" ")); pushLabeledSummaryPart(parts, "failure", failure); if (failure !== null || isFailureLikeStatus(status)) pushLabeledSummaryPart(parts, "message", message); pushLabeledSummaryPart(parts, "trace", fields.get("traceId") ?? fields.get("lastTraceId") ?? null, true); pushLabeledSummaryPart(parts, "session", fields.get("sessionId") ?? fields.get("selectedAgentSessionId") ?? null, true); pushLabeledSummaryPart(parts, "conversation", fields.get("conversationId") ?? fields.get("selectedConversationId") ?? null, true); pushLabeledSummaryPart(parts, "thread", fields.get("threadId") ?? null, true); pushLabeledSummaryPart(parts, "provider", fields.get("providerProfile") ?? fields.get("backendProfile") ?? fields.get("profile") ?? null); pushLabeledSummaryPart(parts, "route", partialRouteSummary(fields)); pushLabeledSummaryPart(parts, "pipeline", fields.get("pipelineRun") ?? fields.get("pipelineRunName") ?? null, true); pushLabeledSummaryPart(parts, "run", fields.get("runId") ?? null, true); pushLabeledSummaryPart(parts, "cmd", fields.get("commandId") ?? null, true); pushLabeledSummaryPart(parts, "job", fields.get("jobName") ?? fields.get("runnerJobId") ?? null, true); pushLabeledSummaryPart(parts, "lane", fields.get("lane") ?? null); pushLabeledSummaryPart(parts, "ns", fields.get("namespace") ?? null); pushLabeledSummaryPart(parts, "actor", fields.get("username") ?? fields.get("displayName") ?? null); pushLabeledSummaryPart(parts, "auth", fields.get("authMethod") ?? null); if (failure === null && !isFailureLikeStatus(status)) pushLabeledSummaryPart(parts, "message", message); return parts.length > 0 ? parts.slice(0, 10).join("; ") : null; } export function partialRouteSummary(fields: Map): string | null { const method = fields.get("method") ?? null; const path = fields.get("path") ?? null; if (method !== null && path !== null) return `${method} ${path}`; return fields.get("url") ?? fields.get("baseUrl") ?? null; } export function jsonLineField(line: string): { key: string; value: string } | null { const match = line.trim().replace(/,$/u, "").match(/^"([^"]+)":\s*(.+)$/u); if (match === null) return null; return { key: match[1], value: cleanJsonScalar(match[2]) }; } export function cleanJsonScalar(raw: string): string { const trimmed = raw.trim(); if (trimmed === "null" || trimmed === "{" || trimmed === "[" || trimmed === "}" || trimmed === "]") return ""; if (trimmed.startsWith('"') && trimmed.endsWith('"')) { try { const parsed = JSON.parse(trimmed) as unknown; return typeof parsed === "string" ? parsed.trim() : String(parsed); } catch { return trimmed.slice(1, -1).trim(); } } return trimmed; } export function cleanCommandOutputLine(line: string): string { const trimmed = line.replace(/,$/u, "").trim(); const jsonField = trimmed.match(/^"([^"]+)":\s*(.+)$/u); if (jsonField === null) return trimmed; const key = jsonField[1]; const value = jsonField[2].replace(/^"|"$/gu, "").trim(); return `${key}=${value}`; } export function isLowSignalCommandOutputLine(line: string): boolean { const trimmed = line.replace(/,$/u, "").trim(); if (trimmed === "{" || trimmed === "}" || trimmed === "}," || trimmed === "];" || trimmed === "[") return true; const field = jsonLineField(trimmed); return field !== null && isLowSignalJsonField(field.key); } export function isLowSignalJsonField(key: string): boolean { return key === "generatedAt" || key === "cli" || key === "version" || key === "valuesRedacted" || key === "secretMaterialStored"; } export function eventText(value: unknown): string | null { const direct = stringOrNull(value); if (direct !== null && direct.trim().length > 0) return direct.trim(); const nested = record(value); return stringOrNull(nested.text) ?? stringOrNull(nested.message) ?? stringOrNull(nested.reason) ?? stringOrNull(nested.summary); } export function formatCountField(label: string, value: unknown): string | null { if (typeof value === "number" && Number.isFinite(value)) return `${label}=${value}`; if (typeof value === "string" && value.length > 0) return `${label}=${value}`; return null; } export function renderResourceTable(items: Record[], wide: boolean): string { const headers = wide ? ["NAME", "STATE", "QUEUE", "LANE", "PROFILE", "RUN", "CMD", "RJOB", "SESSION", "AGE", "TITLE"] : ["NAME", "STATE", "QUEUE", "LANE", "RUN", "CMD", "RJOB", "SESSION", "AGE", "ATTENTION"]; const rows = items.map((item) => { if (wide) { return [ resourceName(item), displayValue(item.state), displayValue(item.queue), displayValue(item.lane), displayValue(item.profile), shortId(stringOrDash(item.run)), shortId(stringOrDash(item.command)), shortId(stringOrDash(item.runnerjob)), shortId(stringOrDash(item.session)), displayValue(item.age), truncateOneLine(displayValue(item.title), 48), ]; } return [ resourceName(item), displayValue(item.state), displayValue(item.queue), displayValue(item.lane), shortId(stringOrDash(item.run)), shortId(stringOrDash(item.command)), shortId(stringOrDash(item.runnerjob)), shortId(stringOrDash(item.session)), displayValue(item.age), displayValue(item.attention), ]; }); return renderTable(headers, rows); } export function renderTaskDescription(task: Record, options: AgentRunResourceOptions): string { const attempt = record(task.latestAttempt); const taskId = displayValue(task.id); const runId = stringOrNull(attempt.runId); const commandId = stringOrNull(attempt.commandId); const runnerJobId = stringOrNull(attempt.runnerJobId); const sessionId = stringOrNull(attempt.sessionId) ?? stringOrNull(record(task.sessionRef).sessionId); const lines = [ `Name: task/${taskId}`, `State: ${displayValue(task.state)} Attention: ${displayValue(task.unread === true ? "unread" : "")}`, `Queue: ${displayValue(task.queue)}`, `Lane: ${displayValue(task.lane)} Profile: ${displayValue(task.backendProfile)} Provider: ${displayValue(task.providerId)}`, `Title: ${displayValue(task.title)}`, "", "Latest Attempt:", ` Attempt: ${displayValue(attempt.attemptId)} State: ${displayValue(attempt.state)}`, ` Run: ${runId === null ? "-" : `run/${runId}`}`, ` Command: ${commandId === null ? "-" : `command/${commandId}`}`, ` Runner: ${runnerJobId === null ? "-" : `runnerjob/${runnerJobId}`}`, ` Session: ${sessionId === null ? "-" : `session/${sessionId}`}`, "", "Conditions:", ` Terminal: ${displayValue(task.state)}`, ` Failure: ${displayValue(task.failureKind ?? task.degradedReason ?? "none")}`, " Values: redacted", "", "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, 6); lines.push(...next.map((item) => ` ${item}`)); return lines.join("\n"); } export function compactTaskDescriptionPayload(ref: AgentRunResourceRef, task: Record): Record { const attempt = record(task.latestAttempt); const taskId = stringOrNull(task.id) ?? ref.name; const runId = stringOrNull(attempt.runId); const commandId = stringOrNull(attempt.commandId); const runnerJobId = stringOrNull(attempt.runnerJobId); const sessionId = stringOrNull(attempt.sessionId) ?? stringOrNull(record(task.sessionRef).sessionId); return { kind: ref.kind, name: ref.name, summary: { id: taskId, state: task.state ?? null, queue: task.queue ?? null, lane: task.lane ?? null, backendProfile: task.backendProfile ?? null, providerId: task.providerId ?? null, title: task.title ?? null, unread: task.unread === true, failureKind: task.failureKind ?? task.degradedReason ?? null, }, latestAttempt: { attemptId: attempt.attemptId ?? null, state: attempt.state ?? null, runId, commandId, runnerJobId, sessionId, }, next: { events: runId === null ? null : `bun scripts/cli.ts agentrun events run/${runId} --after-seq 0 --limit 100`, 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 compactAipodSpecDescriptionPayload(ref: AgentRunResourceRef, data: unknown): Record { const root = record(data); const describedItem = record(root.item); const item = Object.keys(describedItem).length > 0 ? describedItem : root; const manifest = record(root.spec); const manifestMetadata = record(manifest.metadata); const manifestSpec = record(manifest.spec); const manifestAipodMetadata = record(manifestSpec.metadata); const model = firstNonEmptyRecord(item.model, manifestSpec.model); const workspaceRef = compactAipodSpecWorkspaceRef(firstNonEmptyRecord(item.workspaceRef, manifestSpec.workspaceRef)); const resourceBundleRef = compactAipodSpecResourceBundleRef(item.resourceBundleRef, manifestSpec.resourceBundleRef); const secretScope = record(record(manifestSpec.executionPolicy).secretScope); const providerCredentialRefs = compactAipodSpecCredentialRefs(item.providerCredentials, secretScope.providerCredentials, "provider"); const toolCredentialRefs = compactAipodSpecCredentialRefs(item.toolCredentials, secretScope.toolCredentials, "tool"); const imageRef = firstNonEmptyRecord(item.imageRef, manifestSpec.imageRef); return { kind: "AipodSpec", name: ref.name, state: boundedAipodSpecText(item.state ?? item.status ?? "available", 80), identity: { declaredName: boundedAipodSpecText(item.name ?? manifestMetadata.name ?? ref.name, 160), displayName: boundedAipodSpecText(item.displayName ?? manifestMetadata.displayName, 200), specHash: boundedAipodSpecText(item.specHash, 160), valuesPrinted: false, }, runtime: { queue: boundedAipodSpecText(item.queue ?? manifestSpec.queue, 120), lane: boundedAipodSpecText(item.lane ?? manifestSpec.lane, 120), backendProfile: boundedAipodSpecText(item.backendProfile ?? item.profile ?? manifestSpec.backendProfile, 160), providerId: boundedAipodSpecText(item.providerId ?? manifestSpec.providerId, 160), model: { name: boundedAipodSpecText(model.model, 200), reasoningEffort: boundedAipodSpecText(model.reasoningEffort, 80), valuesPrinted: false, }, valuesPrinted: false, }, workspaceRef, resourceBundleRef, credentialRefs: { totalCount: providerCredentialRefs.count + toolCredentialRefs.count, provider: providerCredentialRefs, tool: toolCredentialRefs, valuesPrinted: false, }, provenance: { source: boundedAipodSpecText(item.source ?? manifestAipodMetadata.source, 320), apiVersion: boundedAipodSpecText(manifest.apiVersion, 120), sourceKind: boundedAipodSpecText(manifest.kind, 120), imageSourceIdentity: boundedAipodSpecText(imageRef.sourceIdentity, 320), createdAt: boundedAipodSpecText(item.createdAt, 120), updatedAt: boundedAipodSpecText(item.updatedAt, 120), valuesPrinted: false, }, redaction: { credentialValues: "omitted", secretRefsOnly: true, valuesPrinted: false, }, next: { full: `bun scripts/cli.ts agentrun describe aipodspec/${ref.name} --full -o json`, raw: `bun scripts/cli.ts agentrun describe aipodspec/${ref.name} --raw`, }, valuesPrinted: false, }; } export function renderAipodSpecDescription(payload: Record): string { const identity = record(payload.identity); const runtime = record(payload.runtime); const model = record(runtime.model); const workspaceRef = record(payload.workspaceRef); const resourceBundleRef = record(payload.resourceBundleRef); const sourceIdentity = record(resourceBundleRef.sourceIdentity); const credentialRefs = record(payload.credentialRefs); const providerCredentialRefs = record(credentialRefs.provider); const toolCredentialRefs = record(credentialRefs.tool); const provenance = record(payload.provenance); const next = record(payload.next); const lines = [ `Name: aipodspec/${displayValue(payload.name)}`, `State: ${displayValue(payload.state)}`, "", "Identity:", ` Declared: ${displayValue(identity.declaredName)}`, ` Display: ${displayValue(identity.displayName)}`, ` Hash: ${displayValue(identity.specHash)}`, "", "Runtime:", ` Queue: ${displayValue(runtime.queue)}`, ` Lane: ${displayValue(runtime.lane)}`, ` Profile: ${displayValue(runtime.backendProfile)}`, ` Provider: ${displayValue(runtime.providerId)}`, ` Model: ${displayValue(model.name)} reasoning=${displayValue(model.reasoningEffort)}`, "", "Workspace:", ` Kind: ${displayValue(workspaceRef.kind)} path=${displayValue(workspaceRef.path)} branch=${displayValue(workspaceRef.branch)}`, "", "Primary resource bundle:", ` Source: kind=${displayValue(sourceIdentity.kind)} repo=${displayValue(sourceIdentity.repoUrl)} ref=${displayValue(sourceIdentity.ref)} commit=${displayValue(sourceIdentity.commitId)}`, ` References: bundles=${displayValue(resourceBundleRef.bundleCount)} requiredSkills=${displayValue(resourceBundleRef.requiredSkillCount)} promptRefs=${displayValue(resourceBundleRef.promptRefCount)}`, ` Credentials: provider=${displayValue(providerCredentialRefs.count)} tool=${displayValue(toolCredentialRefs.count)} total=${displayValue(credentialRefs.totalCount)}`, ]; const credentialLines = [ ...renderAipodSpecCredentialRefLines(arrayRecords(providerCredentialRefs.items), "provider"), ...renderAipodSpecCredentialRefLines(arrayRecords(toolCredentialRefs.items), "tool"), ]; if (credentialLines.length > 0) lines.push("", "SecretRefs:", ...credentialLines.map((line) => ` ${line}`)); lines.push( "", "Provenance:", ` Source: ${displayValue(provenance.source)}`, ` Manifest: ${displayValue(provenance.apiVersion)} ${displayValue(provenance.sourceKind)}`, ` Image source: ${displayValue(provenance.imageSourceIdentity)}`, ` Updated: ${displayValue(provenance.updatedAt)}`, " Values printed: false", "", "Next:", ` ${displayValue(next.full)}`, ` ${displayValue(next.raw)}`, ); return lines.join("\n"); } function compactAipodSpecWorkspaceRef(value: Record): Record | null { if (Object.keys(value).length === 0) return null; return { kind: boundedAipodSpecText(value.kind, 80), path: boundedAipodSpecText(value.path, 320), branch: boundedAipodSpecText(value.branch, 160), repo: boundedAipodSpecRepoUrl(value.repo ?? value.repoUrl), commitId: boundedAipodSpecText(value.commitId, 160), valuesPrinted: false, }; } function compactAipodSpecResourceBundleRef(primaryValue: unknown, fallbackValue: unknown): Record | null { const value = firstNonEmptyRecord(primaryValue, fallbackValue); if (Object.keys(value).length === 0) return null; return { sourceIdentity: { kind: boundedAipodSpecText(value.kind, 80), repoUrl: boundedAipodSpecRepoUrl(value.repoUrl), ref: boundedAipodSpecText(value.ref, 160), commitId: boundedAipodSpecText(value.commitId, 160), valuesPrinted: false, }, bundleCount: aipodSpecReferenceCount(value.bundles), requiredSkillCount: aipodSpecReferenceCount(value.requiredSkills), promptRefCount: aipodSpecReferenceCount(value.promptRefs), valuesPrinted: false, }; } function compactAipodSpecCredentialRefs(primaryValue: unknown, fallbackValue: unknown, kind: "provider" | "tool"): { count: number; items: Record[]; omittedCount: number; valuesPrinted: false } { const primary = record(primaryValue); const primaryItems = arrayRecords(primary.items ?? primaryValue); const fallbackItems = arrayRecords(fallbackValue); const sourceItems = primaryItems.length > 0 ? primaryItems : fallbackItems; const count = Object.keys(primary).length > 0 ? aipodSpecReferenceCount(primaryValue) : sourceItems.length; const items = sourceItems.slice(0, 6).map((item) => { const nestedSecretRef = record(item.secretRef); const projection = record(item.projection); return { ...(kind === "provider" ? { profile: boundedAipodSpecText(item.profile, 160) } : { tool: boundedAipodSpecText(item.tool, 160), purpose: boundedAipodSpecText(item.purpose, 200), }), secretRef: { namespace: boundedAipodSpecText(item.namespace ?? nestedSecretRef.namespace, 160), name: boundedAipodSpecText(item.name ?? nestedSecretRef.name, 200), keys: boundedAipodSpecStrings(item.keys ?? nestedSecretRef.keys, 6, 80), valuesPrinted: false, }, ...(kind === "tool" ? { projection: { kind: boundedAipodSpecText(projection.kind, 80), envName: boundedAipodSpecText(projection.envName, 160), secretKey: boundedAipodSpecText(projection.secretKey, 160), mountPath: boundedAipodSpecText(projection.mountPath, 320), valuesPrinted: false, }, } : {}), valuesPrinted: false, }; }); return { count, items, omittedCount: Math.max(0, count - items.length), valuesPrinted: false, }; } function renderAipodSpecCredentialRefLines(items: Record[], kind: "provider" | "tool"): string[] { return items.map((item) => { const secretRef = record(item.secretRef); const namespace = stringOrNull(secretRef.namespace); const name = displayValue(secretRef.name); const reference = namespace === null ? name : `${namespace}/${name}`; const keys = Array.isArray(secretRef.keys) ? secretRef.keys.map(String).join(",") : "-"; const identity = kind === "provider" ? `profile=${displayValue(item.profile)}` : `tool=${displayValue(item.tool)} purpose=${displayValue(item.purpose)}`; return `${kind} ${identity} secret=${reference} keys=${keys}`; }); } function firstNonEmptyRecord(...values: unknown[]): Record { for (const value of values) { const candidate = record(value); if (Object.keys(candidate).length > 0) return candidate; } return {}; } function aipodSpecReferenceCount(value: unknown): number { if (Array.isArray(value)) return value.length; const summary = record(value); const count = summary.count; if (typeof count === "number" && Number.isFinite(count) && count >= 0) return Math.floor(count); return arrayRecords(summary.items).length; } function boundedAipodSpecStrings(value: unknown, limit: number, maxChars: number): string[] { if (!Array.isArray(value)) return []; return value .slice(0, limit) .map((item) => boundedAipodSpecText(item, maxChars)) .filter((item): item is string => item !== null); } function boundedAipodSpecText(value: unknown, maxChars: number): string | null { const text = stringOrNull(value); return text === null ? null : truncateOneLine(text, maxChars); } function boundedAipodSpecRepoUrl(value: unknown): string | null { const sanitized = taskInputRepoUrl(value); return typeof sanitized === "string" ? truncateOneLine(sanitized, 320) : null; } export function taskInputDescriptionPayload(ref: AgentRunResourceRef, task: Record): Record { 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 | 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 | null { if (!isRecord(value)) return null; return { ...pickCompact(value, ["namespace", "name", "keys", "mountPath"]), valuesPrinted: false, }; } function taskInputResourceBundleRef(value: unknown): Record | 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 | 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 | null { if (!isRecord(value)) return null; return pickCompact(value, ["sessionId", "conversationId", "threadId", "expiresAt"]); } function taskInputMetadata(value: unknown): Record { if (!isRecord(value)) return {}; return pickCompact(value, ["aipod", "source", "aipodSpecHash", "aipodImageRef"]); } function taskInputImageRef(value: unknown): Record | 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 ""; } } export function unwrapTaskDetail(data: Record): Record { const task = record(data.task); if (Object.keys(task).length > 0) return task; return data; } export function renderGenericDescription(ref: AgentRunResourceRef, data: unknown): string { const value = record(data); const lines = [ `Name: ${ref.kind}/${ref.name}`, `State: ${displayValue(value.state ?? value.status ?? value.phase ?? value.executionState ?? value.terminalStatus ?? "-")}`, ]; const identity = renderResourceIdentityLines(ref, value); if (identity.length > 0) lines.push("", ...identity); const runnerObservation = renderRunnerJobObservationLines(ref, value); if (runnerObservation.length > 0) lines.push("", "Runtime observation:", ...runnerObservation.map((line) => ` ${line}`)); const supervisor = renderSupervisorLines(ref, value); if (supervisor.length > 0) lines.push("", "Supervisor:", ...supervisor.map((line) => ` ${line}`)); 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"); } export function renderRunnerJobObservationLines(ref: AgentRunResourceRef, value: Record): string[] { if (ref.kind !== "runnerjob") return []; const observation = record(value.runtimeObservation); const runner = record(observation.runner); if (Object.keys(observation).length === 0) return []; if (Object.keys(runner).length === 0) { return [ `Available: false`, `Reason: ${displayValue(observation.reason ?? "runtime-observation-unavailable")}`, ]; } const podPhases = Array.isArray(runner.podPhases) ? runner.podPhases.map(String).join(",") : "-"; const waitingReasons = Array.isArray(runner.waitingReasons) ? runner.waitingReasons.map(String).join(",") : "-"; return [ `Manager phase: ${displayValue(value.managerPhase ?? "-")}`, `Pod phase: ${podPhases || "-"}`, `Waiting: ${waitingReasons || "-"}`, `AgeMs: ${displayValue(runner.lastActiveAgeMs ?? "-")}`, `Run: ${displayValue(runner.runStatus ?? "-")} claimedBy=${displayValue(runner.runClaimedBy ?? "-")}`, `Command: ${displayValue(runner.commandState ?? "-")}`, `Heartbeat: ${displayValue(runner.heartbeatAt ?? "-")} ageMs=${displayValue(runner.heartbeatAgeMs ?? "-")}`, `Classification: ${displayValue(runner.classification ?? "-")} protected=${displayValue(runner.protectedActive ?? "-")}`, ]; } export function renderResourceIdentityLines(ref: AgentRunResourceRef, value: Record): string[] { const supervisor = record(value.supervisor); const fields: [string, unknown][] = [ ["Run", firstKnownString(value.runId, value.activeRunId, value.lastRunId, supervisor.runId, supervisor.activeRunId, supervisor.lastRunId)], ["Command", firstKnownString(value.commandId, value.activeCommandId, value.lastCommandId, supervisor.commandId, supervisor.activeCommandId, supervisor.lastCommandId)], ["RunnerJob", value.runnerJobId ?? (ref.kind === "runnerjob" ? value.id : null)], ["Session", firstKnownString(value.sessionId, supervisor.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); } export function firstKnownString(...values: unknown[]): string | null { for (const value of values) { const text = stringOrNull(value); if (text !== null && text.length > 0) return text; } return null; } export function formatResourceField(label: string, value: unknown): string | null { const text = stringOrNull(value); if (text === null || text.length === 0) return null; return `${label}: ${text}`; } export function renderSupervisorLines(ref: AgentRunResourceRef, value: Record): string[] { if (ref.kind !== "session" && ref.kind !== "task") return []; const supervisor = record(value.supervisor); const liveness = record(value.liveness); const source = Object.keys(supervisor).length > 0 ? supervisor : liveness; if (Object.keys(source).length === 0) return []; const timeoutBudget = record(source.timeoutBudget); const terminalClassification = record(source.terminalClassification); const runId = firstKnownString(source.activeRunId, source.runId, source.lastRunId, value.activeRunId, value.runId, value.lastRunId); const commandId = firstKnownString(source.activeCommandId, source.commandId, source.lastCommandId, value.activeCommandId, value.commandId, value.lastCommandId); const lines: string[] = []; const executionState = firstKnownString(source.executionState, value.executionState); if (executionState !== null) lines.push(`Execution: ${executionState}`); if (typeof source.active === "boolean") lines.push(`Active: ${String(source.active)}`); if (runId !== null) lines.push(`Run: run/${runId}`); if (commandId !== null) lines.push(`Command: command/${commandId}`); const phase = firstKnownString(source.phase, liveness.phase); const lastEventAgeMs = typeof source.lastEventAgeMs === "number" ? source.lastEventAgeMs : typeof liveness.lastEventAgeMs === "number" ? liveness.lastEventAgeMs : null; if (phase !== null || lastEventAgeMs !== null) lines.push(`Liveness: ${displayValue(phase)}${lastEventAgeMs === null ? "" : ` ageMs=${String(lastEventAgeMs)}`}`); const timeoutKind = firstKnownString(timeoutBudget.timeoutKind); const timeoutState = firstKnownString(timeoutBudget.state); const remainingMs = typeof timeoutBudget.remainingMs === "number" ? timeoutBudget.remainingMs : null; if (timeoutKind !== null || timeoutState !== null || remainingMs !== null) lines.push(`Timeout: ${displayValue(timeoutKind)} / ${displayValue(timeoutState)}${remainingMs === null ? "" : ` remainingMs=${String(remainingMs)}`}`); const category = firstKnownString(terminalClassification.category); const reason = firstKnownString(terminalClassification.reason); if (category !== null || reason !== null) lines.push(`Classification: ${displayValue(category)}${reason === null ? "" : `; ${reason}`}`); return lines; }