// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. resource-actions module for scripts/src/agentrun.ts.

// Moved mechanically from scripts/src/agentrun.ts:303-686 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 { AgentRunResourceOptions, AgentRunResourceRef, AgentRunResourceVerb } from "./utils";
import { agentRunDryRunPlan, readAgentRunNativeDevelopmentClientConfig } from "./config";
import { renderEventDetail } from "./event-detail";
import { isHelpArg, renderAgentRunHelp } from "./entry";
import { agentRunExplain, arrayRecords, shortId } from "./options";
import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactAipodSpecDescriptionPayload, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderAipodSpecDescription, 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, withAgentRunNativeClientConfig, withAgentRunRestTarget } from "./rest-bridge";
import { renderSessionSendError } from "./session-send-render";
import { prepareAgentRunTargetTaskPreflight } from "./target-execution";
import { runnerJobObservationScript, type RunnerJobObservationIdentity } from "./runner-observation";
import { isChineseTaskTitle, validateChineseTaskTitle, withAgentRunTargetTaskPreflight } from "./target-task";
import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, record, stringOrNull } from "./utils";

export const AGENTRUN_RESOURCE_TRANSPORT_SCOPE = "[--native-development | [--target <NODE>|--node <NODE>] [--lane <lane>]]";

export function agentRunGetKindHelp(kindRaw: string): string {
  const kind = parseResourceKind(kindRaw);
  if (kind === "task") return `Usage: bun scripts/cli.ts agentrun get tasks [--queue commander] [--state running,pending,completed,failed] [--unread] [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "attempt") return `Usage: bun scripts/cli.ts agentrun get attempts --task <taskId> [--cursor <retry-index>] [--limit 20] [--full] [-o json|yaml] [--raw] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "session") return `Usage: bun scripts/cli.ts agentrun get sessions [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "run") return `Usage: bun scripts/cli.ts agentrun get runs --task <taskId> [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "command") return `Usage: bun scripts/cli.ts agentrun get commands --run <runId> [--command <commandId>] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "runnerjob") return `Usage: bun scripts/cli.ts agentrun get runnerjobs --run <runId> --command <commandId> [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  if (kind === "aipodspec") return `Usage: bun scripts/cli.ts agentrun get aipodspecs [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`;
  return "Unknown resource. Supported: tasks, attempts, sessions, runs, commands, runnerjobs, aipodspecs.";
}

export async function runAgentRunResourceCommand(config: UniDeskConfig | null, verb: AgentRunResourceVerb, action: string | undefined, actionArgs: string[], canonicalArgs: string[]): Promise<RenderedCliResult> {
  if (isHelpArg(action) || actionArgs.some(isHelpArg)) return renderAgentRunHelp(canonicalArgs);
  const resourceArgs = action === undefined ? actionArgs : [action, ...actionArgs];
  const command = `agentrun ${canonicalArgs.join(" ")}`.trim();
  let options: AgentRunResourceOptions;
  try {
    options = parseResourceOptions(resourceArgs);
    validateResourceOptionsForVerb(verb, options);
    validateResourceActionBeforeTarget(verb, action, actionArgs, options);
  } catch (error) {
    const validationError = error instanceof AgentRunRestError
      ? error
      : new AgentRunRestError("validation-failed", error instanceof Error ? error.message : String(error));
    return renderAgentRunRestError(command, validationError, resourceErrorOutputOptions(resourceArgs));
  }
  const bridgeActionArgs = stripAgentRunResourceWrapperArgs(actionArgs);
  try {
    const targetTaskPreflight = await prepareAgentRunTargetTaskPreflight(config, canonicalArgs, options);
    const nativeClientConfig = options.nativeDevelopment ? readAgentRunNativeDevelopmentClientConfig() : null;
    const restTarget = resolveAgentRunRestTarget(options.nativeDevelopment ? null : config, options);
    return await withAgentRunTargetTaskPreflight(targetTaskPreflight, async () => await withAgentRunNativeClientConfig(nativeClientConfig, async () => await withAgentRunRestTarget(restTarget, async () => {
      if (verb === "explain") return renderedCliResult(true, command, agentRunExplain(action ?? "task", resourceArgs, options));
      if (verb === "get") return await resourceGet(config, command, action, bridgeActionArgs, options);
      if (verb === "describe") return await resourceDescribe(config, command, action, bridgeActionArgs, options);
      if (verb === "events") return await resourceEvents(config, command, action, bridgeActionArgs, options);
      if (verb === "logs") return await resourceLogs(config, command, action, bridgeActionArgs, options);
      if (verb === "result") return await resourceResult(config, command, action, bridgeActionArgs, options);
      if (verb === "ack") return await resourceAck(config, command, action, bridgeActionArgs, options);
      if (verb === "cancel") return await resourceCancel(config, command, action, bridgeActionArgs, options);
      if (verb === "retry") return await resourceRetry(config, command, action, bridgeActionArgs, options);
      if (verb === "dispatch") return await resourceDispatch(config, command, action, bridgeActionArgs, options);
      if (verb === "create") return await resourceCreate(config, command, action, bridgeActionArgs, options);
      if (verb === "apply") return await resourceApply(config, command, bridgeActionArgs, options);
      if (verb === "send") return await resourceSessionPromptCommand(config, command, action, bridgeActionArgs, options);
      return renderedCliResult(false, command, `Unsupported AgentRun resource command. Try: bun scripts/cli.ts agentrun --help`);
    })));
  } catch (error) {
    if (error instanceof AgentRunRestError) {
      if (verb === "send") return renderSessionSendError(command, error.toPayload(command), options);
      return renderAgentRunRestError(command, error, options);
    }
    return renderedCliResult(false, command, `Error: ${error instanceof Error ? error.message : String(error)}`);
  }
  return renderedCliResult(false, command, `Unsupported AgentRun resource command. Try: bun scripts/cli.ts agentrun --help`);
}

function validateResourceActionBeforeTarget(
  verb: AgentRunResourceVerb,
  action: string | undefined,
  args: string[],
  options: AgentRunResourceOptions,
): void {
  if (verb === "retry") {
    if (action === undefined || action.startsWith("-")) throw new AgentRunRestError("validation-failed", "retry requires task/<taskId>");
    let ref: AgentRunResourceRef;
    try {
      ref = parseResourceRef(action, args, "task");
    } catch {
      throw new AgentRunRestError("validation-failed", "retry requires task/<taskId>");
    }
    if (ref.kind !== "task") throw new AgentRunRestError("validation-failed", "retry supports task/<taskId>");
    if (options.idempotencyKey === null) throw new AgentRunRestError("validation-failed", "retry requires --idempotency-key <key>");
    if (options.reason === null) throw new AgentRunRestError("validation-failed", "retry requires --reason <text>");
  }
  if (verb === "get" && parseResourceKind(action) === "attempt" && options.taskId === null) {
    throw new AgentRunRestError("validation-failed", "get attempts requires --task <taskId>");
  }
  if (verb === "create" && parseResourceKind(action) === "task" && options.aipod === "Artificer") {
    validateChineseTaskTitle(options.title);
  }
}

function resourceErrorOutputOptions(args: string[]): AgentRunResourceOptions {
  const options = parseResourceOptions([]);
  options.raw = args.includes("--raw");
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    const value = arg === "-o" || arg === "--output"
      ? args[index + 1]
      : arg.startsWith("--output=") || arg.startsWith("-o=")
        ? arg.slice(arg.indexOf("=") + 1)
        : null;
    if (value === "json" || value === "yaml") options.output = value;
  }
  return options;
}

export function stripAgentRunResourceWrapperArgs(args: string[]): string[] {
  const result: string[] = [];
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    if (arg === "-o" || arg === "--output") {
      index += 1;
      continue;
    }
    if (arg === "--node" || arg === "--lane") {
      index += 1;
      continue;
    }
    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" || arg === "--input" || arg === "--native-development") continue;
    result.push(arg);
  }
  return result;
}

export function parseResourceOptions(args: string[]): AgentRunResourceOptions {
  const options: AgentRunResourceOptions = {
    output: "human",
    full: false,
    raw: false,
    debug: false,
    taskInput: false,
    limit: 20,
    cursor: null,
    queue: null,
    state: null,
    unread: false,
    readerId: "cli",
    taskId: null,
    runId: null,
    commandId: null,
    sessionId: null,
    afterSeq: null,
    expect: null,
    timeoutMs: 120_000,
    eventDetailSeq: null,
    aggregate: null,
    tail: null,
    fullText: false,
    reason: null,
    dryRun: false,
    file: null,
    aipod: null,
    title: null,
    idempotencyKey: null,
    promptStdin: false,
    nativeDevelopment: false,
    node: null,
    lane: null,
    target: null,
    targetWorkspace: null,
    repository: null,
    ref: null,
    mdtodoId: null,
    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", "--expect", "--timeout", "--detail-seq", "--aggregate", "--tail", "--reason", "-f", "--file", "--filename", "--aipod", "--title", "--idempotency-key", "--node", "--lane", "--target", "--target-workspace", "--repo", "--ref", "--mdtodo-id"]);
  const booleanFlags = new Set(["--full", "--raw", "--debug", "--input", "--unread", "--dry-run", "--full-text", "--prompt-stdin", "--stdin", "--native-development"]);
  for (let index = 0; index < args.length; index += 1) {
    const arg = args[index] ?? "";
    if (!arg.startsWith("-")) continue;
    if (arg.startsWith("--") && arg.includes("=")) {
      const [flag, ...rest] = arg.split("=");
      applyResourceOption(options, flag, rest.join("="));
      continue;
    }
    if (booleanFlags.has(arg)) {
      applyResourceOption(options, arg, null);
      continue;
    }
    if (valueFlags.has(arg)) {
      const value = args[index + 1];
      if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`);
      applyResourceOption(options, arg, value);
      index += 1;
      continue;
    }
  }
  if (options.target !== null) {
    if (options.node !== null && options.node !== options.target) {
      throw new Error(`--target ${options.target} conflicts with --node ${options.node}`);
    }
    options.node = options.target;
  }
  if (options.taskInput && (options.full || options.raw)) {
    const conflictingOptions = ["--input", options.full ? "--full" : null, options.raw ? "--raw" : null]
      .filter((value): value is string => value !== null);
    throw new AgentRunRestError(
      "validation-failed",
      `${conflictingOptions.join(" with ")} is not allowed: --input is the bounded SecretRef-only TaskInput projection and cannot be combined with complete resource disclosure.`,
      {
        details: {
          conflictingOptions,
          recoveryActions: [
            "Use --input without --full or --raw for the bounded SecretRef-only TaskInput projection.",
            "Remove --input when explicitly requesting the complete resource with --full or --raw.",
          ],
          valuesPrinted: false,
        },
      },
    );
  }
  if (options.eventDetailSeq !== null) {
    const conflictingOptions = ["--after-seq", "--expect", "--timeout", "--limit", "--full", "--raw"]
      .filter((flag) => resourceOptionPresent(args, flag));
    if (conflictingOptions.length > 0) {
      throw new AgentRunRestError(
        "validation-failed",
        `--detail-seq cannot be combined with ${conflictingOptions.join(", ")}: bounded EventDetail and complete event disclosure are separate read modes.`,
        {
          details: {
            conflictingOptions: ["--detail-seq", ...conflictingOptions],
            recoveryActions: [
              "Use --detail-seq <seq> without --after-seq, --expect, --timeout, --limit, --full, or --raw for the bounded Secret-safe EventDetail projection.",
              "Use --after-seq <seq-1> --limit 1 --full -o json or --raw only when explicitly requesting the complete durable event.",
            ],
            valuesPrinted: false,
          },
        },
      );
    }
  }
  if (options.aggregate !== null) {
    const conflictingOptions = ["--after-seq", "--expect", "--timeout", "--detail-seq", "--limit", "--full", "--raw"]
      .filter((flag) => resourceOptionPresent(args, flag));
    if (conflictingOptions.length > 0) {
      throw new AgentRunRestError(
        "validation-failed",
        `--aggregate ${options.aggregate} cannot be combined with ${conflictingOptions.join(", ")}.`,
        {
          details: {
            conflictingOptions: ["--aggregate", ...conflictingOptions],
            recoveryActions: ["Use --aggregate reuse by itself; the CLI reads all bounded event pages and emits only the reuse summary."],
            valuesPrinted: false,
          },
        },
      );
    }
  }
  if (resourceOptionPresent(args, "--timeout") && options.expect === null) {
    throw new AgentRunRestError(
      "validation-failed",
      "--timeout requires --expect <N>; timeout controls the manager-held long poll and is not a client-side polling interval.",
      {
        details: {
          recoveryActions: ["Use --expect <N> [--timeout <duration>] or remove --timeout for an immediate paged read."],
          valuesPrinted: false,
        },
      },
    );
  }
  return options;
}

function validateResourceOptionsForVerb(verb: AgentRunResourceVerb, options: AgentRunResourceOptions): void {
  if (options.nativeDevelopment) {
    if (options.node !== null || options.lane !== null || options.target !== null) {
      throw new AgentRunRestError("validation-failed", "--native-development cannot be combined with --target, --node, or --lane");
    }
    if (!new Set<AgentRunResourceVerb>(["get", "describe", "events", "logs", "result"]).has(verb)) {
      throw new AgentRunRestError("validation-failed", "--native-development is read-only and supports get, describe, events, logs, and result");
    }
  }
  if (options.eventDetailSeq !== null && verb !== "events") {
    throw new AgentRunRestError(
      "validation-failed",
      "--detail-seq is supported only by agentrun events run/<runId>.",
      {
        details: {
          recoveryActions: ["Use: bun scripts/cli.ts agentrun events run/<runId> --detail-seq <seq> [-o json|yaml]."],
          valuesPrinted: false,
        },
      },
    );
  }
  if (options.aggregate !== null && verb !== "events") {
    throw new AgentRunRestError("validation-failed", "--aggregate is supported only by agentrun events run/<runId>.");
  }
  if (options.expect !== null && verb !== "events") {
    throw new AgentRunRestError(
      "validation-failed",
      "--expect and --timeout are supported only by agentrun events run/<runId>.",
      {
        details: {
          recoveryActions: ["Use: bun scripts/cli.ts agentrun events run/<runId> --after-seq <N> --expect <N> [--timeout 120s]."],
          valuesPrinted: false,
        },
      },
    );
  }
}

function resourceOptionPresent(args: string[], flag: string): boolean {
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
}

export function applyResourceOption(options: AgentRunResourceOptions, flag: string, value: string | null): void {
  if (flag === "-o" || flag === "--output") {
    if (value !== "human" && value !== "wide" && value !== "name" && value !== "json" && value !== "yaml") throw new Error(`${flag} must be one of human,wide,name,json,yaml`);
    options.output = value;
    return;
  }
  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;
  else if (flag === "--prompt-stdin" || flag === "--stdin") options.promptStdin = true;
  else if (flag === "--native-development") options.nativeDevelopment = true;
  else if (flag === "--aggregate") {
    if (value !== "reuse") throw new Error("--aggregate must be reuse");
    options.aggregate = value;
  }
  else if (flag === "--limit") options.limit = parseNonNegativeInt(value, "--limit", 20, 500);
  else if (flag === "--cursor") options.cursor = parseNonNegativeInt(value, "--cursor", 0, Number.MAX_SAFE_INTEGER);
  else if (flag === "--queue") options.queue = requiredValue(value, flag);
  else if (flag === "--state") options.state = requiredValue(value, flag);
  else if (flag === "--reader-id") options.readerId = requiredValue(value, flag);
  else if (flag === "--task" || flag === "--task-id") options.taskId = requiredValue(value, flag);
  else if (flag === "--run" || flag === "--run-id") options.runId = requiredValue(value, flag);
  else if (flag === "--command" || flag === "--command-id") options.commandId = requiredValue(value, flag);
  else if (flag === "--session" || flag === "--session-id") options.sessionId = requiredValue(value, flag);
  else if (flag === "--after-seq") options.afterSeq = parseNonNegativeInt(value, "--after-seq", 0, Number.MAX_SAFE_INTEGER);
  else if (flag === "--expect") options.expect = parsePositiveInt(value, "--expect");
  else if (flag === "--timeout") options.timeoutMs = parseDurationMs(value, "--timeout");
  else if (flag === "--detail-seq") options.eventDetailSeq = parsePositiveInt(value, "--detail-seq");
  else if (flag === "--tail") options.tail = parseNonNegativeInt(value, "--tail", 100, 1000);
  else if (flag === "--reason") options.reason = requiredValue(value, flag);
  else if (flag === "-f" || flag === "--file" || flag === "--filename") options.file = requiredValue(value, flag);
  else if (flag === "--aipod") options.aipod = requiredValue(value, flag);
  else if (flag === "--title") options.title = requiredValue(value, flag);
  else if (flag === "--idempotency-key") options.idempotencyKey = requiredValue(value, flag);
  else if (flag === "--node") options.node = requiredValue(value, flag);
  else if (flag === "--lane") options.lane = requiredValue(value, flag);
  else if (flag === "--target") options.target = requiredValue(value, flag);
  else if (flag === "--target-workspace") options.targetWorkspace = requiredValue(value, flag);
  else if (flag === "--repo") options.repository = requiredValue(value, flag);
  else if (flag === "--ref") options.ref = requiredValue(value, flag);
  else if (flag === "--mdtodo-id") options.mdtodoId = requiredValue(value, flag);
}

function parsePositiveInt(raw: string | null, flag: string): number {
  if (raw === null || raw.length === 0) throw new Error(`${flag} requires a value`);
  const value = Number(raw);
  if (!Number.isSafeInteger(value) || value < 1) throw new Error(`${flag} must be a positive safe integer`);
  return value;
}

export function parseDurationMs(raw: string | null, flag: string): number {
  if (raw === null || raw.length === 0) throw new Error(`${flag} requires a duration`);
  const match = /^(\d+)(ms|s|m)$/u.exec(raw.trim());
  if (match === null) throw new Error(`${flag} must use a positive integer duration with ms, s, or m units, such as 500ms, 120s, or 3m`);
  const amount = Number(match[1]);
  const multiplier = match[2] === "ms" ? 1 : match[2] === "s" ? 1_000 : 60_000;
  const value = amount * multiplier;
  if (!Number.isSafeInteger(value) || value < 1 || value > 3_600_000) throw new Error(`${flag} must be between 1ms and 60m`);
  return value;
}

export function parseNonNegativeInt(raw: string | null, flag: string, defaultValue: number, maxValue: number): number {
  if (raw === null || raw.length === 0) return defaultValue;
  const value = Number(raw);
  if (!Number.isInteger(value) || value < 0) throw new Error(`${flag} must be a non-negative integer`);
  return Math.min(value, maxValue);
}

export function requiredValue(value: string | null, flag: string): string {
  if (value === null || value.length === 0) throw new Error(`${flag} requires a value`);
  return value;
}

export async function resourceGet(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const kind = parseResourceKind(action);
  if (kind === null) throw new Error("get requires a resource: tasks|attempts|sessions|runs|commands|runnerjobs|aipodspecs");
  let result: Record<string, unknown>;
  if (kind === "task") {
    const taskListArgs = options.unread
      ? ["commander", "--reader-id", options.readerId, "--limit", String(options.limit)]
      : [
          "list",
          "--state",
          taskListState(options),
          "--limit",
          String(options.queue === null ? options.limit : Math.max(options.limit, 100)),
          ...(options.queue === null ? [] : ["--queue", options.queue]),
        ];
    result = await runAgentRunRestCommand(config, "queue", taskListArgs);
    return renderResourceResult(command, result, options, "Task", normalizeTaskItems(innerData(result), options).slice(0, options.limit));
  }
  if (kind === "session") {
    result = await runAgentRunRestCommand(config, "sessions", ["ps", "--limit", String(options.limit)]);
    return renderResourceResult(command, result, options, "Session", normalizeSessionItems(innerData(result)).slice(0, options.limit));
  }
  if (kind === "attempt") {
    if (options.taskId === null) throw new AgentRunRestError("validation-failed", "get attempts requires --task <taskId>");
    result = await runAgentRunRestCommand(config, "queue", [
      "attempts",
      options.taskId,
      ...(options.cursor === null ? [] : ["--cursor", String(options.cursor)]),
      "--limit",
      String(options.limit),
    ]);
    return renderQueueAttemptList(command, result, options, options.taskId, normalizeQueueAttemptItems(innerData(result)).slice(0, options.limit));
  }
  if (kind === "aipodspec") {
    result = await runAgentRunRestCommand(config, "aipod-specs", ["list"]);
    return renderResourceResult(command, result, options, "AipodSpec", normalizeAipodSpecItems(innerData(result)).slice(0, options.limit));
  }
  if (kind === "run" && options.taskId !== null) {
    result = await runAgentRunRestCommand(config, "queue", ["show", options.taskId]);
    const task = record(innerData(result));
    return renderResourceResult(command, result, options, "Run", normalizeAttemptResources(task, "run"));
  }
  if (kind === "command" && options.runId !== null && options.commandId !== null) {
    result = await runAgentRunRestCommand(config, "commands", ["show", options.commandId, "--run-id", options.runId]);
    return renderResourceResult(command, result, options, "Command", normalizeSingleCommand(innerData(result)));
  }
  if (kind === "runnerjob" && options.runId !== null && options.commandId !== null) {
    result = await runAgentRunRestCommand(config, "runner", ["jobs", "--run-id", options.runId, "--command-id", options.commandId]);
    return renderResourceResult(command, result, options, "RunnerJob", normalizeRunnerJobItems(innerData(result)));
  }
  throw new Error(`get ${kind}s requires more context; use --task, --run/--command, or describe <kind>/<id>`);
}

export async function resourceRetry(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  if (action === undefined || action.startsWith("-")) throw new AgentRunRestError("validation-failed", "retry requires task/<taskId>");
  let ref: AgentRunResourceRef;
  try {
    ref = parseResourceRef(action, args, "task");
  } catch {
    throw new AgentRunRestError("validation-failed", "retry requires task/<taskId>");
  }
  if (ref.kind !== "task") throw new AgentRunRestError("validation-failed", "retry supports task/<taskId>");
  if (options.idempotencyKey === null) throw new AgentRunRestError("validation-failed", "retry requires --idempotency-key <key>");
  if (options.reason === null) throw new AgentRunRestError("validation-failed", "retry requires --reason <text>");
  const result = await runAgentRunRestCommand(config, "queue", [
    "retry",
    ref.name,
    "--idempotency-key",
    options.idempotencyKey,
    "--reason",
    options.reason,
    ...(options.dryRun ? ["--dry-run"] : []),
  ]);
  return renderQueueRetrySummary(command, result, options, ref.name);
}

export async function resourceDescribe(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const titleContext = await resolveTaskTitleContext(config, action);
  const ref = titleContext === null ? parseResourceRef(action, args) : { kind: "task", name: titleContext.taskId } as const;
  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 }
        : 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") {
    const result = await runAgentRunRestCommand(config, "runs", ["show", ref.name, ...(options.full ? ["--full"] : [])]);
    return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
  }
  if (ref.kind === "command") {
    const runId = options.runId ?? requiredContext("command describe", "--run <runId>");
    const result = await runAgentRunRestCommand(config, "commands", ["show", ref.name, "--run-id", runId, ...(options.full ? ["--full"] : [])]);
    return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
  }
  if (ref.kind === "runnerjob") {
    const runId = options.runId ?? requiredContext("runnerjob describe", "--run <runId>");
    const result = await runAgentRunRestCommand(config, "runner", ["job-status", ref.name, "--run-id", runId]);
    const observed = await observeRunnerJob(config, options, ref.name, result);
    return renderDescribe(command, observed, options, ref, renderGenericDescription(ref, innerData(observed)));
  }
  if (ref.kind === "session") {
    const result = await runAgentRunRestCommand(config, "sessions", ["show", ref.name, ...(options.full ? ["--full"] : [])]);
    return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
  }
  if (ref.kind === "aipodspec") {
    const result = await runAgentRunRestCommand(config, "aipod-specs", ["show", ref.name, ...(options.full ? ["--full"] : [])]);
    if (options.raw || options.full) return renderDescribe(command, result, options, ref, renderGenericDescription(ref, innerData(result)));
    const payload = compactAipodSpecDescriptionPayload(ref, innerData(result));
    if (options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output, result.ok !== false);
    return renderedCliResult(result.ok !== false, command, renderAipodSpecDescription(payload));
  }
  throw new Error(`unsupported describe kind: ${ref.kind}`);
}

async function observeRunnerJob(
  config: UniDeskConfig | null,
  options: AgentRunResourceOptions,
  runnerJobId: string,
  managerResult: Record<string, unknown>,
): Promise<Record<string, unknown>> {
  const managerResource = record(innerData(managerResult));
  const target = resolveAgentRunRestTarget(config, options);
  if (target === null) {
    return {
      ...managerResult,
      data: {
        ...managerResource,
        managerPhase: managerResource.phase ?? null,
        runtimeObservation: {
          ok: false,
          reason: "yaml-lane-target-unavailable",
          mutation: false,
          valuesPrinted: false,
        },
      },
    };
  }
  const identity = runnerJobObservationIdentity(managerResource, runnerJobId, target.spec.runtime.namespace);
  if (identity === null) {
    return {
      ...managerResult,
      ok: false,
      data: {
        ...managerResource,
        managerPhase: managerResource.phase ?? null,
        runtimeObservation: {
          ok: false,
          reason: "manager-runner-identity-incomplete-or-mismatched",
          target: {
            node: target.spec.nodeId,
            lane: target.spec.lane,
            namespace: target.spec.runtime.namespace,
          },
          mutation: false,
          valuesPrinted: false,
        },
      },
    };
  }
  const observationResult = await capture(target.config, target.spec.nodeKubeRoute, [
    "sh",
    "--",
    runnerJobObservationScript(target.spec, identity),
  ]);
  const observation = captureJsonPayload(observationResult);
  const runner = record(observation.runner);
  const podPhases = Array.isArray(runner.podPhases) ? runner.podPhases.map(String) : [];
  const observedState = stringOrNull(runner.state) ?? (podPhases.includes("Pending")
    ? "Pending"
    : podPhases.includes("Running")
      ? "Running"
      : podPhases.includes("Failed")
        ? "Failed"
        : podPhases.includes("Succeeded")
          ? "Succeeded"
          : stringOrNull(runner.classification) ?? stringOrNull(managerResource.phase) ?? "Unknown");
  const observationFound = Object.keys(runner).length > 0;
  const runtimeObservation = {
    ok: observationResult.exitCode === 0 && observation.ok === true && observationFound,
    reason: observationResult.exitCode === 0 ? observation.reason ?? (observationFound ? null : "runner-job-runtime-observation-not-found") : "runner-job-runtime-observation-failed",
    observedAt: observation.observedAt ?? null,
    target: {
      node: target.spec.nodeId,
      lane: target.spec.lane,
      namespace: target.spec.runtime.namespace,
    },
    managerFacts: observation.managerFacts ?? null,
    kubernetesFacts: observation.kubernetesFacts ?? null,
    runner: observationFound ? runner : null,
    mutation: false,
    valuesPrinted: false,
  };
  return {
    ...managerResult,
    ok: managerResult.ok !== false && runtimeObservation.ok,
    data: {
      ...managerResource,
      state: observedState,
      managerPhase: managerResource.phase ?? null,
      runtimeObservation,
      valuesPrinted: false,
    },
    runnerObservationBridge: {
      mode: "yaml-lane-exact-runner-job-read-only",
      configPath: target.configPath,
      node: target.spec.nodeId,
      lane: target.spec.lane,
      capture: compactCapture(observationResult, { full: observationResult.exitCode !== 0, stdoutTailChars: 1200, stderrTailChars: 2000 }),
      valuesPrinted: false,
    },
  };
}

function runnerJobObservationIdentity(managerResource: Record<string, unknown>, runnerJobId: string, namespace: string): RunnerJobObservationIdentity | null {
  const identity = {
    runnerJobId: stringOrNull(managerResource.id),
    runId: stringOrNull(managerResource.runId),
    commandId: stringOrNull(managerResource.commandId),
    runnerId: stringOrNull(managerResource.runnerId),
    namespace: stringOrNull(managerResource.namespace),
    jobName: stringOrNull(managerResource.jobName),
  };
  if (identity.runnerJobId !== runnerJobId || identity.namespace !== namespace) return null;
  if (identity.runId === null || identity.commandId === null || identity.runnerId === null || identity.jobName === null) return null;
  return identity as RunnerJobObservationIdentity;
}

export async function resourceEvents(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const titleContext = await resolveTaskTitleContext(config, action);
  const ref = titleContext === null ? parseResourceRef(action, args, "run") : null;
  const runId = titleContext === null
    ? ref?.kind === "run" ? ref.name : options.runId ?? requiredContext("events", "--run <runId>")
    : requiredTitleResource(titleContext, "runId", "events");
  if (options.aggregate === "reuse") {
    const summary = await aggregateAgentRunReuseEvents(config, runId);
    if (options.output === "json" || options.output === "yaml") return renderMachine(command, summary, options.output, summary.reusePassed === true);
    return renderedCliResult(summary.reusePassed === true, command, renderAgentRunReuseSummary(summary));
  }
  if (options.eventDetailSeq !== null) {
    const detailSeq = options.eventDetailSeq;
    const result = await runAgentRunRestCommand(config, "runs", ["events", runId, "--after-seq", String(detailSeq - 1), "--limit", "1"]);
    const item = arrayRecords(record(innerData(result)).items ?? innerData(result))[0];
    if (item === undefined || nonNegativeIntegerOrNull(item.seq) !== detailSeq) {
      throw new AgentRunRestError(
        "not-found",
        `run/${runId} has no durable event at seq ${detailSeq}.`,
        {
          details: {
            runId,
            requestedSeq: detailSeq,
            returnedSeq: item === undefined ? null : nonNegativeIntegerOrNull(item.seq),
            recoveryActions: [`List bounded events first: bun scripts/cli.ts agentrun events run/${runId} --after-seq ${Math.max(0, detailSeq - 20)} --limit 20.`],
            valuesPrinted: false,
          },
        },
      );
    }
    return renderEventDetail(command, result, options, runId, item);
  }
  const requestedLimit = Math.max(1, options.limit);
  const effectiveLimit = options.full || options.raw ? requestedLimit : Math.min(requestedLimit, 20);
  const requestLimit = options.full || options.raw ? effectiveLimit : Math.min(500, effectiveLimit + 1);
  const eventArgs = ["events", runId, "--after-seq", String(options.afterSeq ?? 0), "--limit", String(requestLimit)];
  if (options.expect !== null) eventArgs.push("--expect", String(options.expect), "--timeout-ms", String(options.timeoutMs));
  const result = await runAgentRunRestCommand(config, "runs", eventArgs);
  if (options.raw) return renderMachine(command, result, "json", result.ok !== false);
  if (options.full) return renderMachine(command, result, options.output === "yaml" ? "yaml" : "json", result.ok !== false);

  const fetchedItems = arrayRecords(record(innerData(result)).items ?? innerData(result));
  const visibleItems = fetchedItems.slice(0, effectiveLimit);
  const hasMore = fetchedItems.length > visibleItems.length;
  const lastVisibleSeq = visibleItems.length === 0 ? null : nonNegativeIntegerOrNull(visibleItems.at(-1)?.seq);
  const responseNextAfterSeq = nonNegativeIntegerOrNull(record(innerData(result)).nextAfterSeq);
  const nextAfterSeq = hasMore ? lastVisibleSeq : responseNextAfterSeq ?? options.afterSeq ?? 0;
  const waitData = record(innerData(result));
  const observed = nonNegativeIntegerOrNull(waitData.observed);
  if (options.expect !== null && (typeof waitData.timedOut !== "boolean" || observed === null)) {
    throw new AgentRunRestError(
      "schema-mismatch",
      "AgentRun manager long-poll response must include boolean timedOut and non-negative observed fields.",
      {
        details: {
          runId,
          expect: options.expect,
          timeoutMs: options.timeoutMs,
          returnedTimedOutType: typeof waitData.timedOut,
          returnedObserved: waitData.observed ?? null,
          valuesPrinted: false,
        },
      },
    );
  }
  const waitDisclosure = options.expect === null ? null : {
    expect: options.expect,
    timeoutMs: options.timeoutMs,
    timedOut: waitData.timedOut === true,
    observed: observed ?? fetchedItems.length,
    completionReason: stringOrNull(waitData.completionReason) ?? stringOrNull(waitData.reason),
  };
  const targetArgs = [
    options.node === null ? null : `--node ${options.node}`,
    options.lane === null ? null : `--lane ${options.lane}`,
  ].filter((value): value is string => value !== null).join(" ");
  const detailCommand = `bun scripts/cli.ts agentrun events run/${runId} --detail-seq <item.seq>${targetArgs.length === 0 ? "" : ` ${targetArgs}`}`;
  return renderEventLike(command, result, options, "Event", normalizeEventItems({ items: visibleItems }, runId), runId, {
    requestedLimit,
    effectiveLimit,
    fetchedCount: fetchedItems.length,
    hasMore,
    nextAfterSeq,
    detailCommand,
    wait: waitDisclosure,
  });
}

const AGENTRUN_REUSE_EVENT_PAGE_SIZE = 500;
const AGENTRUN_REUSE_EVENT_MAX = 10_000;

export async function aggregateAgentRunReuseEvents(config: UniDeskConfig | null, runId: string): Promise<Record<string, unknown>> {
  const events: Record<string, unknown>[] = [];
  let afterSeq = 0;
  let pageCount = 0;
  while (events.length < AGENTRUN_REUSE_EVENT_MAX) {
    const result = await runAgentRunRestCommand(config, "runs", ["events", runId, "--after-seq", String(afterSeq), "--limit", String(AGENTRUN_REUSE_EVENT_PAGE_SIZE)]);
    const page = arrayRecords(record(innerData(result)).items ?? innerData(result));
    pageCount += 1;
    events.push(...page);
    if (page.length < AGENTRUN_REUSE_EVENT_PAGE_SIZE) break;
    const nextAfterSeq = nonNegativeIntegerOrNull(page.at(-1)?.seq);
    if (nextAfterSeq === null || nextAfterSeq <= afterSeq) {
      throw new AgentRunRestError("schema-mismatch", `AgentRun event pagination did not advance after seq ${afterSeq}.`);
    }
    afterSeq = nextAfterSeq;
  }
  if (events.length >= AGENTRUN_REUSE_EVENT_MAX) {
    throw new AgentRunRestError("schema-mismatch", `run/${runId} reuse aggregation exceeded the ${AGENTRUN_REUSE_EVENT_MAX} event safety bound.`);
  }

  const phases = events.map((event) => record(event.payload)).map((payload) => stringOrNull(payload.phase));
  const phaseCount = (phase: string): number => phases.filter((value) => value === phase).length;
  const commandIds = events
    .filter((event) => record(event.payload).phase === "command-created")
    .map((event) => stringOrNull(record(event.payload).commandId))
    .filter((value): value is string => value !== null)
    .filter((value, index, values) => values.indexOf(value) === index);
  const terminalStatuses = events
    .filter((event) => event.type === "terminal_status")
    .map((event) => {
      const payload = record(event.payload);
      return {
        commandId: stringOrNull(payload.commandId),
        status: stringOrNull(payload.terminalStatus) ?? stringOrNull(payload.status) ?? stringOrNull(payload.state),
      };
    });
  const runnerJobEvent = events.find((event) => record(event.payload).phase === "runner-job-created");
  const runnerJobPayload = record(runnerJobEvent?.payload);
  const runnerJobCreated = phaseCount("runner-job-created");
  const runnerSourceFetchStarted = phaseCount("runner-source-fetch-started");
  const resourceBundleMaterialized = phaseCount("resource-bundle-materialized");
  const codexAppServerReused = phaseCount("codex-app-server:reused");
  const threadResumeCompleted = phaseCount("thread/resume:completed");
  const backendTurnStarted = phaseCount("backend-turn-started");
  const commandTerminal = phaseCount("command-terminal");
  const expectedReuseCount = Math.max(0, commandIds.length - 1);
  const reusePassed = commandIds.length > 0
    && runnerJobCreated === 1
    && runnerSourceFetchStarted === 1
    && resourceBundleMaterialized === 1
    && codexAppServerReused === expectedReuseCount
    && threadResumeCompleted === expectedReuseCount
    && backendTurnStarted === commandIds.length
    && commandTerminal === commandIds.length
    && terminalStatuses.length === commandIds.length;

  return {
    kind: "AgentRunReuseSummary",
    runId,
    eventCount: events.length,
    pageCount,
    commandCount: commandIds.length,
    commandIds,
    runnerJobCreated,
    runnerJob: {
      id: stringOrNull(runnerJobPayload.runnerJobId),
      jobName: stringOrNull(runnerJobPayload.jobName),
      runnerId: stringOrNull(runnerJobPayload.runnerId),
    },
    runnerSourceFetchStarted,
    resourceBundleMaterialized,
    codexAppServerReused,
    threadResumeCompleted,
    backendTurnStarted,
    commandTerminal,
    terminalStatuses,
    reusePassed,
    valuesPrinted: false,
  };
}

function renderAgentRunReuseSummary(summary: Record<string, unknown>): string {
  const runnerJob = record(summary.runnerJob);
  const commandIds = Array.isArray(summary.commandIds)
    ? summary.commandIds.filter((value): value is string => typeof value === "string")
    : [];
  return [
    `AgentRun reuse ${summary.reusePassed === true ? "passed" : "failed"}`,
    `Run: ${String(summary.runId)}`,
    `Events: ${String(summary.eventCount)} across ${String(summary.pageCount)} page(s)`,
    `Commands: ${String(summary.commandCount)} (${commandIds.join(", ")})`,
    `Runner Job: created=${String(summary.runnerJobCreated)} jobName=${stringOrNull(runnerJob.jobName) ?? "-"}`,
    `Assembly: sourceFetch=${String(summary.runnerSourceFetchStarted)} materialized=${String(summary.resourceBundleMaterialized)}`,
    `Reuse: appServer=${String(summary.codexAppServerReused)} threadResume=${String(summary.threadResumeCompleted)}`,
    `Turns: started=${String(summary.backendTurnStarted)} terminal=${String(summary.commandTerminal)} terminalStatus=${Array.isArray(summary.terminalStatuses) ? summary.terminalStatuses.length : 0}`,
  ].join("\n");
}

export async function resourceLogs(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const titleContext = await resolveTaskTitleContext(config, action);
  const ref = titleContext === null
    ? parseResourceRef(action, args, "session")
    : { kind: "session", name: requiredTitleResource(titleContext, "sessionId", "logs") } as const;
  if (ref.kind !== "session") throw new Error("logs currently requires session/<sessionId> or an exact Chinese task title");
  const effectiveLimit = options.tail ?? options.limit;
  const logArgs = ["output", ref.name, "--limit", String(effectiveLimit)];
  if (options.afterSeq !== null) logArgs.push("--after-seq", String(options.afterSeq));
  if (options.fullText) logArgs.push("--full-text");
  let result: Record<string, unknown>;
  if (options.afterSeq === null && options.tail !== null) {
    result = await resourceLogsTailResult(config, ref.name, effectiveLimit, options.fullText);
  } else {
    result = await runAgentRunRestCommand(config, "sessions", logArgs);
  }
  return renderEventLike(command, result, { ...options, limit: effectiveLimit }, "Log", normalizeLogItems(innerData(result)), ref.name);
}

export async function resourceLogsTailResult(config: UniDeskConfig | null, sessionId: string, limit: number, fullText: boolean): Promise<Record<string, unknown>> {
  const session = record(innerData(await runAgentRunRestCommand(config, "sessions", ["show", sessionId])));
  const lastSeq = nonNegativeIntegerOrNull(session.lastEventSeq) ?? 0;
  let window = Math.max(limit * 4, 100);
  let result: Record<string, unknown> | null = null;
  let data: Record<string, unknown> = {};
  for (let attempt = 0; attempt < 6; attempt += 1) {
    const afterSeq = Math.max(0, lastSeq - window);
    const queryLimit = Math.max(limit, Math.min(1000, window));
    const args = ["output", sessionId, "--limit", String(queryLimit), "--after-seq", String(afterSeq)];
    if (fullText) args.push("--full-text");
    result = await runAgentRunRestCommand(config, "sessions", args);
    data = record(innerData(result));
    const items = arrayRecords(data.items);
    if (items.length >= limit || afterSeq === 0) break;
    window *= 2;
  }
  if (result === null) return await runAgentRunRestCommand(config, "sessions", ["output", sessionId, "--limit", String(limit), "--after-seq", "0"]);
  const items = arrayRecords(data.items);
  const tailedItems = items.slice(-limit);
  const lastReturnedSeq = tailedItems.length > 0 ? nonNegativeIntegerOrNull(tailedItems[tailedItems.length - 1]?.seq) : null;
  return {
    ...result,
    data: {
      ...data,
      items: tailedItems,
      count: tailedItems.length,
      cursor: lastReturnedSeq === null ? data.cursor : String(lastReturnedSeq),
      clientTail: {
        requested: limit,
        sourceLastEventSeq: lastSeq,
        scanned: items.length,
        valuesPrinted: false,
      },
    },
  };
}

export async function resourceResult(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const titleContext = await resolveTaskTitleContext(config, action);
  if (titleContext !== null) {
    const runId = requiredTitleResource(titleContext, "runId", "result");
    const commandId = requiredTitleResource(titleContext, "commandId", "result");
    const result = await runAgentRunRestCommand(config, "runs", ["result", runId, "--command-id", commandId]);
    return renderResultSummary(command, result, options, { kind: "run", name: runId });
  }
  const ref = parseResourceRef(action, args);
  if (ref.kind === "run") {
    const commandId = options.commandId ?? requiredContext("run result", "--command <commandId>");
    const result = await runAgentRunRestCommand(config, "runs", ["result", ref.name, "--command-id", commandId]);
    return renderResultSummary(command, result, options, ref);
  }
  if (ref.kind === "command") {
    const runId = options.runId ?? requiredContext("command result", "--run <runId>");
    const result = await runAgentRunRestCommand(config, "commands", ["result", ref.name, "--run-id", runId]);
    return renderResultSummary(command, result, options, ref);
  }
  throw new Error("result supports run/<runId> or command/<commandId>");
}

interface AgentRunTaskTitleContext {
  title: string;
  taskId: string;
  runId: string | null;
  commandId: string | null;
  sessionId: string | null;
}

async function resolveTaskTitleContext(config: UniDeskConfig | null, action: string | undefined): Promise<AgentRunTaskTitleContext | null> {
  if (!isChineseTaskTitle(action)) return null;
  const title = validateChineseTaskTitle(action);
  const result = await runAgentRunRestCommand(config, "queue", ["list", "--limit", "500"]);
  const tasks = arrayRecords(record(innerData(result)).items ?? innerData(result));
  const matches = tasks.filter((task) => normalizeStoredTaskTitle(task) === title);
  if (matches.length === 0) {
    throw new AgentRunRestError("not-found", `未找到中文标题完全匹配“${title}”的 Artificer 任务。`);
  }
  if (matches.length > 1) {
    throw new AgentRunRestError("validation-failed", `中文标题“${title}”匹配到多个任务，不能猜测。`, {
      details: {
        title,
        candidates: matches.map((task) => ({ id: stringOrNull(task.id), state: stringOrNull(task.state ?? task.status) })),
        valuesPrinted: false,
      },
    });
  }
  const task = matches[0] ?? {};
  const attempt = record(task.latestAttempt);
  const taskId = stringOrNull(task.id);
  if (taskId === null) throw new AgentRunRestError("schema-mismatch", `中文标题“${title}”匹配的任务缺少 id。`);
  return {
    title,
    taskId,
    runId: stringOrNull(attempt.runId ?? task.runId),
    commandId: stringOrNull(attempt.commandId ?? task.commandId),
    sessionId: stringOrNull(attempt.sessionId ?? record(task.sessionRef).sessionId ?? task.sessionId),
  };
}

function normalizeStoredTaskTitle(task: Record<string, unknown>): string | null {
  const title = stringOrNull(task.title ?? record(task.metadata).title ?? record(record(task.sessionRef).metadata).title);
  return title === null ? null : title.trim().replace(/\s+/gu, " ");
}

function requiredTitleResource(context: AgentRunTaskTitleContext, key: "runId" | "commandId" | "sessionId", operation: string): string {
  const value = context[key];
  if (value !== null) return value;
  throw new AgentRunRestError("not-found", `中文标题“${context.title}”对应任务尚无可用于 ${operation} 的 ${key}。`);
}

export async function resourceAck(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const ref = parseResourceRef(action, args, "task");
  const result = ref.kind === "task"
    ? await runAgentRunRestCommand(config, "queue", ["read", ref.name, "--reader-id", options.readerId])
    : ref.kind === "session"
      ? await runAgentRunRestCommand(config, "sessions", ["read", ref.name, "--reader-id", options.readerId])
      : null;
  if (result === null) throw new Error("ack supports task/<taskId> or session/<sessionId>");
  return renderMutationSummary(command, result, options, `Acknowledged ${ref.kind}/${shortId(ref.name)}`);
}

export async function resourceCancel(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
  const ref = parseResourceRef(action, args, "task");
  const cancelArgs = ["cancel", ref.name];
  if (options.reason !== null) cancelArgs.push("--reason", options.reason);
  if (ref.kind === "command") cancelArgs.push("--run-id", options.runId ?? requiredContext("command cancel", "--run <runId>"));
  if (options.dryRun) {
    const result = agentRunResourceCancelDryRunPlan(config, ref, options, rerunWithoutDryRun(command));
    return renderMutationSummary(command, result, options, `Planned cancel ${ref.kind}/${shortId(ref.name)}`, [rerunWithoutDryRun(command)]);
  }
  const result = ref.kind === "task"
    ? await runAgentRunRestCommand(config, "queue", cancelArgs)
    : ref.kind === "session"
      ? await runAgentRunRestCommand(config, "sessions", cancelArgs)
      : ref.kind === "run"
        ? await runAgentRunRestCommand(config, "runs", cancelArgs)
        : ref.kind === "command"
          ? await runAgentRunRestCommand(config, "commands", cancelArgs)
          : null;
  if (result === null) throw new Error("cancel supports task/<taskId>, session/<sessionId>, run/<runId>, or command/<commandId>");
  return renderMutationSummary(command, result, options, `${options.dryRun ? "Planned cancel" : "Cancel requested"} ${ref.kind}/${shortId(ref.name)}`, options.dryRun ? [rerunWithoutDryRun(command)] : undefined);
}

export function agentRunResourceCancelDryRunPlan(config: UniDeskConfig | null, ref: AgentRunResourceRef, options: AgentRunResourceOptions, confirmCommand: string): Record<string, unknown> {
  const body: Record<string, unknown> = {};
  if (options.reason !== null) body.reason = options.reason;
  const cancelLifecycle = agentRunCancelLifecycleDryRunDisclosure(config, ref, options);
  if (ref.kind === "task") return agentRunDryRunPlan("task-cancel", `/api/v1/queue/tasks/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand, "POST", { cancelLifecycle });
  if (ref.kind === "session") return agentRunDryRunPlan("session-cancel", `/api/v1/sessions/${encodeURIComponent(ref.name)}/control`, { action: "cancel", ...body }, confirmCommand, "POST", { cancelLifecycle });
  if (ref.kind === "run") return agentRunDryRunPlan("run-cancel", `/api/v1/runs/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand, "POST", { cancelLifecycle });
  if (ref.kind === "command") {
    const runId = options.runId ?? requiredContext("command cancel", "--run <runId>");
    return agentRunDryRunPlan("command-cancel", `/api/v1/commands/${encodeURIComponent(ref.name)}/cancel`, body, confirmCommand, "POST", {
      commandRef: { runId, commandId: ref.name, valuesPrinted: false },
      cancelLifecycle,
    });
  }
  throw new Error("cancel supports task/<taskId>, session/<sessionId>, run/<runId>, or command/<commandId>");
}

export function agentRunCancelLifecycleDryRunDisclosure(config: UniDeskConfig | null, ref: AgentRunResourceRef, options: AgentRunResourceOptions): Record<string, unknown> {
  const target = resolveAgentRunCancelPolicyTarget(config, options);
  const policy = target?.spec.deployment.runner.cancelLifecycle ?? null;
  return {
    specRefs: ["PJ2026-01020108", "PJ2026-01020305", "PJ2026-01060305"],
    authority: agentRunCancelAuthorityDisclosure(target),
    targetRef: {
      kind: ref.kind,
      name: ref.name,
      runId: ref.kind === "command" ? options.runId : options.runId ?? null,
      valuesPrinted: false,
    },
    cascadeScope: agentRunCancelCascadeScope(ref.kind),
    terminalAuthority: "AgentRun Core canceled terminal/result event",
    expectedStages: policy?.eventStages ?? [],
    runnerAbort: policy === null ? null : agentRunCancelRunnerAbortDisclosure(policy),
    fencing: agentRunCancelFencingDisclosure(policy),
    verification: {
      describe: `bun scripts/cli.ts agentrun describe ${ref.kind}/${ref.name}`,
      events: ref.kind === "run" || options.runId !== null ? `bun scripts/cli.ts agentrun events run/${ref.kind === "run" ? ref.name : options.runId} --after-seq 0` : null,
      logs: ref.kind === "session" ? `bun scripts/cli.ts agentrun logs session/${ref.name} --tail 100` : null,
      result: ref.kind === "command" ? `bun scripts/cli.ts agentrun result command/${ref.name} --run ${options.runId ?? "<runId>"}` : null,
      valuesPrinted: false,
    },
    valuesPrinted: false,
  };
}
