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

// Moved mechanically from scripts/src/agentrun.ts:5847-6915 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 { AgentRunClientConfig } from "./config";
import type { GitMirrorOptions } from "./options";
import type { AgentRunFailureKind, AgentRunHttpMethod, AgentRunResourceOptions, AgentRunRestCompatGroup, AgentRunRestCompatOptions, AgentRunRestTarget, AgentRunRestTargetOptions } from "./utils";
import { addAgentRunNotFoundLookupHint, agentRunDryRunPlan, agentRunHasFlag, agentRunLaneRestBridgeMetadata, agentRunOption, agentRunQuery, agentRunRestBridgeMetadata, aipodRenderInputFromArgs, cancelBodyFromArgs, classifyAgentRunProxyFailureKind, cloneJsonRecord, copyAgentRunOptions, defaultAgentRunExecutionPolicy, defaultAgentRunProviderId, jsonInputDisclosureFromArgs, jsonObjectOption, normalizeAgentRunFailureKind, optionalJsonBody, optionalPromptFromArgs, optionalRunnerJsonBody, queueDispatchBodyFromArgs, queueSubmitConfirmCommand, readAgentRunClientConfig, readYamlInputFromArgs, requiredAgentRunOption, requiredJsonBody, resolveAgentRunAuth, resolveAgentRunSessionPolicyTarget, safeAgentRunEnvelope } from "./config";
import { status } from "./control-plane";
import { gitMirrorStatus } from "./git-mirror";
import { arrayRecords, displayValue, isRecord, pickCompact, renderTable, truncateOneLine } from "./options";
import { innerData, pathValue, renderMachine, renderedCliResult } from "./render";
import { parseResourceOptions, stripAgentRunResourceWrapperArgs } from "./resource-actions";
import { AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS, agentRunGitMirrorRetryDelayMs, agentRunGitMirrorRetrySummary, agentRunGitMirrorRetryableFailure, createYamlLaneJobScript, yamlLaneGitMirrorJobManifest, yamlLaneGitMirrorStatusScript, yamlLaneJobProbeScript } from "./secrets";
import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, progressEvent, record, shQuote, sleep, stringOrNull } from "./utils";

export async function readGitMirrorStatus(config: UniDeskConfig, target: { configPath: string; spec: AgentRunLaneSpec }): Promise<Record<string, unknown> & { result: SshCaptureResult; raw: string; summary: Record<string, unknown> }> {
  const spec = target.spec;
  const result = await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneGitMirrorStatusScript(spec)]);
  const raw = result.stdout;
  const summary = captureJsonPayload(result);
  return {
    ok: result.exitCode === 0 && summary.ok !== false,
    configPath: target.configPath,
    target: agentRunLaneSummary(spec),
    namespace: spec.gitMirror.namespace,
    readUrl: spec.gitMirror.readUrl,
    writeUrl: spec.gitMirror.writeUrl,
    raw,
    summary,
    result,
  };
}

export async function runGitMirrorJob(config: UniDeskConfig, action: "sync" | "flush", options: GitMirrorOptions): Promise<Record<string, unknown>> {
  const { configPath, spec } = resolveAgentRunLaneTarget(options);
  const previewJobName = `${action === "sync" ? spec.gitMirror.syncJobPrefix : spec.gitMirror.flushJobPrefix}-${Date.now().toString(36)}`.slice(0, 63);
  const previewManifest = yamlLaneGitMirrorJobManifest(spec, action, previewJobName);
  const command = `agentrun git-mirror ${action}`;
  if (options.dryRun || !options.confirm) {
    return {
      ok: true,
      command,
      mode: "yaml-declared-node-lane",
      configPath,
      target: agentRunLaneSummary(spec),
      dryRun: true,
      namespace: spec.gitMirror.namespace,
      jobName: previewJobName,
      manifest: previewManifest,
      next: { confirm: `bun scripts/cli.ts ${command} --node ${spec.nodeId} --lane ${spec.lane} --confirm` },
    };
  }
  if (!options.wait) {
    const jobName = previewJobName;
    const manifest = yamlLaneGitMirrorJobManifest(spec, action, jobName);
    const created = await capture(config, spec.nodeKubeRoute, ["sh", "--", createYamlLaneJobScript(spec.gitMirror.namespace, jobName, manifest)]);
    const status = await gitMirrorStatus(config, { full: false, raw: false, node: spec.nodeId, lane: spec.lane });
    return {
      ok: created.exitCode === 0,
      command,
      mode: created.exitCode === 0 ? "submitted" : "create-failed",
      configPath,
      target: agentRunLaneSummary(spec),
      dryRun: false,
      namespace: spec.gitMirror.namespace,
      jobName,
      result: compactCapture(created),
      status,
      next: {
        status: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`,
        wait: `bun scripts/cli.ts agentrun git-mirror ${action} --node ${spec.nodeId} --lane ${spec.lane} --confirm --wait`,
      },
    };
  }
  const attempts: Record<string, unknown>[] = [];
  let lastCreated: SshCaptureResult | null = null;
  let lastWait: Record<string, unknown> | null = null;
  for (let attempt = 1; attempt <= AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS; attempt += 1) {
    const jobName = `${action === "sync" ? spec.gitMirror.syncJobPrefix : spec.gitMirror.flushJobPrefix}-${Date.now().toString(36)}-${attempt}`.slice(0, 63);
    const manifest = yamlLaneGitMirrorJobManifest(spec, action, jobName);
    const created = await capture(config, spec.nodeKubeRoute, ["sh", "--", createYamlLaneJobScript(spec.gitMirror.namespace, jobName, manifest)]);
    lastCreated = created;
    if (created.exitCode !== 0) {
      const result = { phase: "create-job", capture: compactCapture(created, { full: true, stdoutTailChars: 4000, stderrTailChars: 4000 }) };
      const retryableFailure = agentRunGitMirrorRetryableFailure(spec, action, attempt, result);
      attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: false, phase: "create-job", retryableFailure, retryExhausted: retryableFailure !== null && record(retryableFailure).retryExhausted === true });
      if (retryableFailure !== null && record(retryableFailure).retryExhausted !== true) {
        progressEvent(`agentrun.git-mirror.${action}.retry`, retryableFailure);
        await sleep(agentRunGitMirrorRetryDelayMs(attempt));
        continue;
      }
      const status = await gitMirrorStatus(config, { full: false, raw: false, node: spec.nodeId, lane: spec.lane });
      return {
        ok: false,
        command,
        mode: "create-failed",
        configPath,
        target: agentRunLaneSummary(spec),
        dryRun: false,
        namespace: spec.gitMirror.namespace,
        jobName,
        result: compactCapture(created),
        retryableFailure,
        retry: agentRunGitMirrorRetrySummary(attempts),
        status,
        next: {
          status: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`,
          wait: `bun scripts/cli.ts agentrun git-mirror ${action} --node ${spec.nodeId} --lane ${spec.lane} --confirm --wait`,
        },
      };
    }
    const wait = await waitForGitMirrorJob(config, spec, action, jobName, options.timeoutSeconds);
    lastWait = wait;
    if (wait.ok === true) {
      attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: true, wait });
      const status = await gitMirrorStatus(config, { full: false, raw: false, node: spec.nodeId, lane: spec.lane });
      return {
        ok: true,
        command,
        mode: "waited",
        configPath,
        target: agentRunLaneSummary(spec),
        dryRun: false,
        namespace: spec.gitMirror.namespace,
        jobName,
        result: compactCapture(created),
        wait,
        retry: agentRunGitMirrorRetrySummary(attempts),
        status,
        next: {
          status: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`,
          flush: action === "sync" && status.summary && record(status.summary).pendingFlush === true ? `bun scripts/cli.ts agentrun git-mirror flush --node ${spec.nodeId} --lane ${spec.lane} --confirm` : null,
        },
      };
    }
    const retryableFailure = agentRunGitMirrorRetryableFailure(spec, action, attempt, wait);
    attempts.push({ attempt, retryLabel: `${attempt}/${AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS}`, jobName, ok: false, wait, retryableFailure, retryExhausted: retryableFailure !== null && record(retryableFailure).retryExhausted === true });
    if (retryableFailure !== null && record(retryableFailure).retryExhausted !== true) {
      progressEvent(`agentrun.git-mirror.${action}.retry`, retryableFailure);
      await sleep(agentRunGitMirrorRetryDelayMs(attempt));
      continue;
    }
    const status = await gitMirrorStatus(config, { full: false, raw: false, node: spec.nodeId, lane: spec.lane });
    return {
      ok: false,
      command,
      mode: "waited",
      configPath,
      target: agentRunLaneSummary(spec),
      dryRun: false,
      namespace: spec.gitMirror.namespace,
      jobName,
      result: compactCapture(created),
      wait,
      retryableFailure,
      retry: agentRunGitMirrorRetrySummary(attempts),
      status,
      next: {
        status: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane}`,
        wait: `bun scripts/cli.ts agentrun git-mirror ${action} --node ${spec.nodeId} --lane ${spec.lane} --confirm --wait`,
      },
    };
  }
  const status = await gitMirrorStatus(config, { full: false, raw: false, node: spec.nodeId, lane: spec.lane });
  return {
    ok: false,
    command,
    mode: "retry-exhausted",
    configPath,
    target: agentRunLaneSummary(spec),
    dryRun: false,
    namespace: spec.gitMirror.namespace,
    result: lastCreated === null ? null : compactCapture(lastCreated),
    wait: lastWait,
    retry: agentRunGitMirrorRetrySummary(attempts),
    status,
    next: { status: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane}` },
  };
}

export async function waitForGitMirrorJob(config: UniDeskConfig, spec: AgentRunLaneSpec, action: "sync" | "flush", jobName: string, timeoutSeconds: number): Promise<Record<string, unknown>> {
  const startedAtMs = Date.now();
  let lastProbe: SshCaptureResult | null = null;
  let polls = 0;
  while (Date.now() - startedAtMs <= timeoutSeconds * 1000) {
    polls += 1;
    lastProbe = await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneJobProbeScript(spec.gitMirror.namespace, jobName)]);
    const summary = captureJsonPayload(lastProbe);
    process.stderr.write(`${JSON.stringify({
      event: `agentrun.git-mirror.${action}.progress`,
      at: new Date().toISOString(),
      stage: "k8s-job",
      node: spec.nodeId,
      lane: spec.lane,
      status: summary.succeeded === true ? "succeeded" : summary.failed === true ? "failed" : "running",
      jobName,
      polls,
      elapsedMs: Date.now() - startedAtMs,
    })}\n`);
    if (summary.succeeded === true) {
      return {
        ok: true,
        action,
        jobName,
        polls,
        elapsedMs: Date.now() - startedAtMs,
        summary,
        probe: compactCapture(lastProbe),
      };
    }
    if (summary.failed === true) {
      return {
        ok: false,
        action,
        jobName,
        polls,
        elapsedMs: Date.now() - startedAtMs,
        degradedReason: "git-mirror-job-failed",
        summary,
        probe: compactCapture(lastProbe),
      };
    }
    await sleep(5_000);
  }
  return {
    ok: false,
    action,
    jobName,
    polls,
    elapsedMs: Date.now() - startedAtMs,
    degradedReason: "git-mirror-job-timeout",
    probe: lastProbe ? compactCapture(lastProbe) : null,
  };
}

export function startAsyncAgentRunJob(name: string, command: string[], note: string): Record<string, unknown> {
  const job = startJob(name, command, note);
  const statusCommand = `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`;
  return {
    ok: true,
    mode: "async-job",
    job,
    statusCommand,
    tailCommand: `tail -f ${job.stdoutFile}`,
    next: {
      status: statusCommand,
      tail: `tail -f ${job.stdoutFile}`,
    },
  };
}

export async function runAgentRunRestCompatCommand(config: UniDeskConfig | null, group: AgentRunRestCompatGroup, args: string[], canonicalArgs: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
  const options = parseAgentRunRestCompatOptions(args);
  const command = `agentrun ${canonicalArgs.join(" ")}`.trim();
  try {
    const raw = await withAgentRunRestTarget(resolveAgentRunRestTarget(config, options), async () => {
      return await runAgentRunRestCommand(config, group, stripAgentRunResourceWrapperArgs(args));
    });
    return renderAgentRunRestCompatResult(command, group, stripAgentRunResourceWrapperArgs(args), raw, options);
  } catch (error) {
    if (error instanceof AgentRunRestError) {
      if (options.raw || options.full || options.output === "json" || options.output === "yaml") {
        return renderMachine(command, error.toPayload(command), options.output === "yaml" ? "yaml" : "json", false);
      }
      return error.toPayload(command);
    }
    throw error;
  }
}

export function parseAgentRunRestCompatOptions(args: string[]): AgentRunRestCompatOptions {
  const resourceOptions = parseResourceOptions(args);
  return {
    node: resourceOptions.node,
    lane: resourceOptions.lane,
    output: resourceOptions.output,
    full: resourceOptions.full,
    raw: resourceOptions.raw,
  };
}

export function renderAgentRunRestCompatResult(command: string, group: AgentRunRestCompatGroup, args: string[], raw: Record<string, unknown>, options: AgentRunRestCompatOptions): Record<string, unknown> | RenderedCliResult {
  if (options.raw || options.full) 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 compatGroup = group === "aipods" ? "aipod-specs" : group;
  const [action, id] = args;
  if (compatGroup === "aipod-specs" && action === "render" && id !== undefined) return renderAgentRunAipodSpecRenderSummary(command, raw, id);
  return raw;
}

export function renderAgentRunAipodSpecRenderSummary(command: string, raw: Record<string, unknown>, requestedAipod: string): RenderedCliResult {
  const data = record(innerData(raw));
  const aipod = record(data.aipod);
  const task = record(data.queueTask);
  const bridge = record(raw.bridge);
  const executionPolicy = record(task.executionPolicy);
  const secretScope = record(executionPolicy.secretScope);
  const resourceBundle = record(task.resourceBundleRef ?? aipod.resourceBundleRef);
  const dispatchDefaults = record(data.dispatchDefaults);
  const runnerJobDefaults = record(dispatchDefaults.runnerJob);
  const node = stringOrNull(bridge.node) ?? stringOrNull(task.providerId) ?? "-";
  const lane = stringOrNull(bridge.lane) ?? stringOrNull(task.lane) ?? "-";
  const namespace = stringOrNull(bridge.namespace) ?? stringOrNull(runnerJobDefaults.namespace) ?? "-";
  const providerCredentials = agentRunRenderedProviderCredentials(secretScope, aipod);
  const toolCredentials = agentRunRenderedToolCredentials(secretScope, aipod);
  const bundleNames = agentRunResourceBundleNames(resourceBundle.bundles, "name");
  const skillNames = agentRunResourceBundleNames(resourceBundle.requiredSkills, "name");
  const targetArgs = agentRunCompatTargetCliArgs(node, lane);
  const lines = [
    "AIPODSPEC RENDER",
    `  AipodSpec: ${displayValue(aipod.name ?? requestedAipod)} hash=${displayValue(aipod.specHash)}`,
    `  Target: node=${displayValue(node)} lane=${displayValue(lane)} namespace=${displayValue(namespace)} bridge=${displayValue(bridge.mode)}`,
    `  TaskPolicy: backendProfile=${displayValue(task.backendProfile)} providerId=${displayValue(task.providerId)} queue=${displayValue(task.queue)} lane=${displayValue(task.lane)}`,
    `  WorkspaceRef: ${agentRunCompactJson(task.workspaceRef, 140)}`,
    `  Execution: ${agentRunCompactJson(pickCompact(executionPolicy, ["sandbox", "approval", "timeoutMs", "network"]), 160)}`,
    "  Values: secret payloads are not printed; valuesPrinted=false",
    "",
    "PROVIDER CREDENTIAL SECRETREFS",
    providerCredentials.length > 0
      ? renderTable(["PROFILE", "NAMESPACE", "SECRET", "KEYS", "VALUES"], providerCredentials.map((credential) => [
        displayValue(credential.profile),
        displayValue(credential.namespace ?? namespace),
        displayValue(credential.name),
        agentRunJoinNames(credential.keys, 5),
        "false",
      ]))
      : "  (none)",
    "",
    "TOOL CREDENTIAL SECRETREFS",
    toolCredentials.length > 0
      ? renderTable(["TOOL", "PURPOSE", "NAMESPACE", "SECRET", "KEYS", "PROJECTION", "VALUES"], toolCredentials.map((credential) => [
        displayValue(credential.tool),
        displayValue(credential.purpose),
        displayValue(credential.namespace ?? namespace),
        displayValue(credential.name),
        agentRunJoinNames(credential.keys, 5),
        agentRunProjectionSummary(credential.projection),
        "false",
      ]))
      : "  (none)",
    "",
    "RESOURCE BUNDLE",
    `  Kind: ${displayValue(resourceBundle.kind)} repo=${displayValue(resourceBundle.repoUrl)} ref=${displayValue(resourceBundle.ref)} commit=${displayValue(resourceBundle.commitId)}`,
    `  Bundles: ${agentRunJoinNames(bundleNames, 6)}`,
    `  RequiredSkills: ${agentRunJoinNames(skillNames, 8)}`,
    `  RunnerJob: namespace=${displayValue(runnerJobDefaults.namespace)} image=${agentRunCompactJson(runnerJobDefaults.imageRef, 120)}`,
    "",
    "DRILL-DOWN",
    `  bun scripts/cli.ts ${command} --full`,
    `  bun scripts/cli.ts ${command} -o json`,
    `  bun scripts/cli.ts agentrun create task --aipod ${requestedAipod}${targetArgs} --prompt-stdin`,
  ];
  return renderedCliResult(raw.ok !== false, command, `${lines.join("\n")}\n`);
}

export function agentRunRenderedProviderCredentials(secretScope: Record<string, unknown>, aipod: Record<string, unknown>): Array<{ profile: unknown; namespace: unknown; name: unknown; keys: string[] }> {
  const scoped = arrayRecords(secretScope.providerCredentials).map((credential) => {
    const secretRef = record(credential.secretRef);
    return {
      profile: credential.profile,
      namespace: secretRef.namespace,
      name: secretRef.name,
      keys: agentRunStringList(secretRef.keys),
    };
  });
  if (scoped.length > 0) return scoped;
  return arrayRecords(record(aipod.providerCredentials).items).map((credential) => ({
    profile: credential.profile,
    namespace: credential.namespace,
    name: credential.name,
    keys: agentRunStringList(credential.keys),
  }));
}

export function agentRunRenderedToolCredentials(secretScope: Record<string, unknown>, aipod: Record<string, unknown>): Array<{ tool: unknown; purpose: unknown; namespace: unknown; name: unknown; keys: string[]; projection: Record<string, unknown> }> {
  const scoped = arrayRecords(secretScope.toolCredentials).map((credential) => {
    const secretRef = record(credential.secretRef);
    return {
      tool: credential.tool,
      purpose: credential.purpose,
      namespace: secretRef.namespace,
      name: secretRef.name,
      keys: agentRunStringList(secretRef.keys),
      projection: record(credential.projection),
    };
  });
  if (scoped.length > 0) return scoped;
  return arrayRecords(record(aipod.toolCredentials).items).map((credential) => ({
    tool: credential.tool,
    purpose: credential.purpose,
    namespace: credential.namespace,
    name: credential.name,
    keys: agentRunStringList(credential.keys),
    projection: record(credential.projection),
  }));
}

export function agentRunResourceBundleNames(value: unknown, key: string): string[] {
  const direct = agentRunStringList(record(value).names);
  if (direct.length > 0) return direct;
  return arrayRecords(value).map((item) => stringOrNull(item[key]) ?? "").filter((item) => item.length > 0);
}

export function agentRunStringList(value: unknown): string[] {
  if (!Array.isArray(value)) return [];
  return value.map((item) => String(item)).filter((item) => item.length > 0);
}

export function agentRunJoinNames(values: string[], maxItems: number): string {
  if (values.length === 0) return "-";
  const visible = values.slice(0, maxItems);
  const suffix = values.length > visible.length ? ` +${values.length - visible.length}` : "";
  return `${visible.join(", ")}${suffix}`;
}

export function agentRunProjectionSummary(value: Record<string, unknown>): string {
  const kind = stringOrNull(value.kind);
  if (kind === "env") return `env:${displayValue(value.envName)}<-${displayValue(value.secretKey)}`;
  if (kind === "volume") return `volume:${displayValue(value.mountPath)}`;
  if (Object.keys(value).length === 0) return "-";
  return agentRunCompactJson(value, 80);
}

export function agentRunCompactJson(value: unknown, maxChars: number): string {
  if (value === undefined || value === null) return "-";
  return truncateOneLine(JSON.stringify(value), maxChars);
}

export function agentRunCompatTargetCliArgs(node: string, lane: string): string {
  const parts: string[] = [];
  if (node !== "-") parts.push("--node", node);
  if (lane !== "-") parts.push("--lane", lane);
  return parts.length === 0 ? "" : ` ${parts.join(" ")}`;
}

export async function runAgentRunRestCommand(config: UniDeskConfig | null, group: AgentRunRestCompatGroup, args: string[]): Promise<Record<string, unknown>> {
  void config;
  const compatGroup = group === "aipods" ? "aipod-specs" : group;
  const [action, id] = args;
  if (compatGroup === "queue") return await runAgentRunQueueRest(action, id, args);
  if (compatGroup === "sessions") return await runAgentRunSessionsRest(action, id, args);
  if (compatGroup === "runs") return await runAgentRunRunsRest(action, id, args);
  if (compatGroup === "commands") return await runAgentRunCommandsRest(action, id, args);
  if (compatGroup === "runner") return await runAgentRunRunnerRest(action, id, args);
  if (compatGroup === "aipod-specs") return await runAgentRunAipodSpecsRest(action, id, args);
  throw new AgentRunRestError("unsupported-version", `unsupported AgentRun REST compatibility group: ${group}`);
}

export async function runAgentRunQueueRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "list") return await agentRunRestRequest("agentrun queue list", "GET", `/api/v1/queue/tasks${agentRunQuery(args, ["queue", "state", "cursor", "limit", "updated-after"])}`);
  if (action === "commander") return await agentRunRestRequest("agentrun queue commander", "GET", `/api/v1/queue/commander${agentRunQuery(args, ["queue", "reader-id"])}`);
  if (action === "stats") return await agentRunRestRequest("agentrun queue stats", "GET", `/api/v1/queue/stats${agentRunQuery(args, ["queue"])}`);
  if (action === "show" && id) return await agentRunRestRequest("agentrun queue show", "GET", `/api/v1/queue/tasks/${encodeURIComponent(id)}`);
  if (action === "submit") return await submitQueueTaskRest(args);
  if (action === "dispatch" && id) return await mutateQueueTaskRest("queue-dispatch", id, "dispatch", queueDispatchBodyFromArgs(args), args);
  if (action === "read" && id) return await mutateQueueTaskRest("queue-read", id, "read", { readerId: agentRunOption(args, "reader-id") ?? "cli" }, args);
  if (action === "cancel" && id) return await mutateQueueTaskRest("queue-cancel", id, "cancel", cancelBodyFromArgs(args), args);
  if (action === "refresh" && id) return await mutateQueueTaskRest("queue-refresh", id, "refresh", {}, args);
  throw new AgentRunRestError("validation-failed", `unsupported queue command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function runAgentRunSessionsRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "ps") return await agentRunRestRequest("agentrun sessions ps", "GET", `/api/v1/sessions${agentRunQuery(args, ["state", "profile", "backend-profile", "reader-id", "cursor", "limit"])}`);
  if (action === "show" && id) return await agentRunRestRequest("agentrun sessions show", "GET", `/api/v1/sessions/${encodeURIComponent(id)}${agentRunQuery(args, ["reader-id"])}`);
  if ((action === "trace" || action === "output") && id) return await agentRunRestRequest(`agentrun sessions ${action}`, "GET", `/api/v1/sessions/${encodeURIComponent(id)}/${action}${agentRunQuery(args, ["after-seq", "limit", "run-id"])}`);
  if (action === "read" && id) {
    const body = { readerId: agentRunOption(args, "reader-id") ?? "cli" };
    if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("session-read", `/api/v1/sessions/${encodeURIComponent(id)}/read`, body, `bun scripts/cli.ts agentrun sessions read ${id} --reader-id ${body.readerId}`);
    return await agentRunRestRequest("agentrun sessions read", "POST", `/api/v1/sessions/${encodeURIComponent(id)}/read`, body);
  }
  if (action === "cancel" && id) {
    const body = { action: "cancel", ...cancelBodyFromArgs(args) };
    if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("session-cancel", `/api/v1/sessions/${encodeURIComponent(id)}/control`, body, `bun scripts/cli.ts agentrun sessions cancel ${id}`);
    return await agentRunRestRequest("agentrun sessions cancel", "POST", `/api/v1/sessions/${encodeURIComponent(id)}/control`, body);
  }
  if (action === "send" && id) return await sessionSendRest(id, args);
  throw new AgentRunRestError("validation-failed", `unsupported sessions command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function runAgentRunRunsRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "create") return await agentRunRestRequest("agentrun runs create", "POST", "/api/v1/runs", await requiredJsonBody(args, "runs create"));
  if (action === "show" && id) return await agentRunRestRequest("agentrun runs show", "GET", `/api/v1/runs/${encodeURIComponent(id)}`);
  if (action === "events" && id) return await agentRunRestRequest("agentrun runs events", "GET", `/api/v1/runs/${encodeURIComponent(id)}/events${agentRunQuery(args, ["after-seq", "limit"])}`);
  if (action === "result" && id) return await agentRunRestRequest("agentrun runs result", "GET", `/api/v1/runs/${encodeURIComponent(id)}/result${agentRunQuery(args, ["command-id", "command"])}`);
  if (action === "cancel" && id) return await agentRunRestRequest("agentrun runs cancel", "POST", `/api/v1/runs/${encodeURIComponent(id)}/cancel`, cancelBodyFromArgs(args));
  throw new AgentRunRestError("validation-failed", `unsupported runs command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function runAgentRunCommandsRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "create" && id) {
    const body = await optionalJsonBody(args);
    if (!body.type) body.type = agentRunOption(args, "type") ?? "turn";
    const idempotencyKey = agentRunOption(args, "idempotency-key");
    if (idempotencyKey) body.idempotencyKey = idempotencyKey;
    return await agentRunRestRequest("agentrun commands create", "POST", `/api/v1/runs/${encodeURIComponent(id)}/commands`, body);
  }
  if (action === "show" && id) {
    const runId = requiredAgentRunOption(args, ["run-id", "run"], "commands show requires --run-id");
    return await agentRunRestRequest("agentrun commands show", "GET", `/api/v1/runs/${encodeURIComponent(runId)}/commands/${encodeURIComponent(id)}`);
  }
  if (action === "result" && id) {
    const runId = requiredAgentRunOption(args, ["run-id", "run"], "commands result requires --run-id");
    return await agentRunRestRequest("agentrun commands result", "GET", `/api/v1/runs/${encodeURIComponent(runId)}/commands/${encodeURIComponent(id)}/result`);
  }
  if (action === "cancel" && id) return await agentRunRestRequest("agentrun commands cancel", "POST", `/api/v1/commands/${encodeURIComponent(id)}/cancel`, cancelBodyFromArgs(args));
  throw new AgentRunRestError("validation-failed", `unsupported commands command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function runAgentRunRunnerRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "jobs") {
    const runId = requiredAgentRunOption(args, ["run-id", "run"], "runner jobs requires --run-id");
    return await agentRunRestRequest("agentrun runner jobs", "GET", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs${agentRunQuery(args, ["command-id", "command"])}`);
  }
  if (action === "job-status") {
    const runId = requiredAgentRunOption(args, ["run-id", "run"], "runner job-status requires --run-id");
    const runnerJobId = id ?? agentRunOption(args, "runner-job-id");
    if (!runnerJobId) return await agentRunRestRequest("agentrun runner job-status", "GET", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs${agentRunQuery(args, ["command-id", "command"])}`);
    return await agentRunRestRequest("agentrun runner job-status", "GET", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs/${encodeURIComponent(runnerJobId)}`);
  }
  if (action === "job") {
    const runId = requiredAgentRunOption(args, ["run-id", "run"], "runner job requires --run-id");
    const body = queueDispatchBodyFromArgs(args);
    const commandId = requiredAgentRunOption(args, ["command-id", "command"], "runner job requires --command-id");
    body.commandId = commandId;
    if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("runner-job", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, body, `bun scripts/cli.ts agentrun runner job --run-id ${runId} --command-id ${commandId}`);
    return await agentRunRestRequest("agentrun runner job", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, body);
  }
  throw new AgentRunRestError("validation-failed", `unsupported runner command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function runAgentRunAipodSpecsRest(action: string | undefined, id: string | undefined, args: string[]): Promise<Record<string, unknown>> {
  if (action === "list") return await agentRunRestRequest("agentrun aipod-specs list", "GET", "/api/v1/aipod-specs");
  if (action === "show" && id) return await agentRunRestRequest("agentrun aipod-specs show", "GET", `/api/v1/aipod-specs/${encodeURIComponent(id)}`);
  if (action === "render" && id) return await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(id)}/render`, await aipodRenderInputFromArgs(args, 2));
  if (action === "apply" || action === "set") {
    const yaml = readYamlInputFromArgs(args);
    const pathValue = id ? `/api/v1/aipod-specs/${encodeURIComponent(id)}` : "/api/v1/aipod-specs";
    const method: AgentRunHttpMethod = id ? "PUT" : "POST";
    const body = { yaml };
    if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("aipod-spec-apply", pathValue, { yamlBytes: Buffer.byteLength(yaml, "utf8") }, `bun scripts/cli.ts agentrun aipod-specs apply${id ? ` ${id}` : ""} --yaml-stdin`, method);
    return await agentRunRestRequest("agentrun aipod-specs apply", method, pathValue, body);
  }
  if ((action === "delete" || action === "rm") && id) return await agentRunRestRequest("agentrun aipod-specs delete", "DELETE", `/api/v1/aipod-specs/${encodeURIComponent(id)}`);
  throw new AgentRunRestError("validation-failed", `unsupported aipod-specs command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}

export async function submitQueueTaskRest(args: string[]): Promise<Record<string, unknown>> {
  const aipod = agentRunOption(args, "aipod") ?? agentRunOption(args, "aipod-spec");
  if (aipod) {
    const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, await aipodRenderInputFromArgs(args, 2));
    const body = normalizeAipodRenderedQueueTask(record(record(innerData(rendered)).queueTask), args, aipod);
    if (Object.keys(body).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask`);
    if (agentRunHasFlag(args, "dry-run")) {
      return agentRunDryRunPlan("queue-submit", "/api/v1/queue/tasks", body, queueSubmitConfirmCommand(args, aipod), "POST", {
        jsonInput: { source: "aipod-spec", aipod, valuesPrinted: false },
        aipodBinding: agentRunAipodBindingDisclosure(body, aipod),
      });
    }
    return await agentRunRestRequest("agentrun queue submit", "POST", "/api/v1/queue/tasks", body);
  }
  const body = await requiredJsonBody(args, "queue submit");
  const idempotencyKey = agentRunOption(args, "idempotency-key");
  if (idempotencyKey) body.idempotencyKey = idempotencyKey;
  if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("queue-submit", "/api/v1/queue/tasks", body, queueSubmitConfirmCommand(args), "POST", { jsonInput: jsonInputDisclosureFromArgs(args) });
  return await agentRunRestRequest("agentrun queue submit", "POST", "/api/v1/queue/tasks", body);
}

export async function mutateQueueTaskRest(action: string, taskId: string, suffix: string, body: Record<string, unknown>, args: string[]): Promise<Record<string, unknown>> {
  const pathValue = `/api/v1/queue/tasks/${encodeURIComponent(taskId)}/${suffix}`;
  if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan(action, pathValue, body, `bun scripts/cli.ts agentrun queue ${suffix} ${taskId}`);
  return await agentRunRestRequest(`agentrun queue ${suffix}`, "POST", pathValue, body);
}

export async function sessionSendRest(sessionId: string, args: string[]): Promise<Record<string, unknown>> {
  const aipod = agentRunOption(args, "aipod") ?? agentRunOption(args, "aipod-spec");
  if (aipod) return await sessionSendWithAipodRest(sessionId, aipod, args);
  const input = await optionalJsonBody(args);
  const prompt = optionalPromptFromArgs(args, 2);
  const sendBody = await sessionSendBodyFromRunInput(sessionId, args, input, sessionSendPayloadFromInput(input, prompt));
  const response = await agentRunRestRequest("agentrun sessions send", "POST", `/api/v1/sessions/${encodeURIComponent(sessionId)}/send`, sendBody);
  return {
    ok: response.ok !== false,
    command: "agentrun sessions send",
    data: {
      ...record(innerData(response)),
      sessionPolicy: agentRunSessionRunPolicyDisclosure(record(sendBody.run)),
    },
    bridge: response.bridge,
  };
}

export function sessionSendPayloadFromInput(input: Record<string, unknown>, prompt: string | null): Record<string, unknown> {
  const payload = record(input.payload);
  if (Object.keys(payload).length > 0) return payload;
  const text = prompt ?? stringOrNull(input.prompt) ?? stringOrNull(input.message) ?? stringOrNull(input.text);
  if (text === null) throw new AgentRunRestError("validation-failed", "send requires payload or non-empty prompt/message/text; use --prompt-stdin, --prompt-file, --prompt, a trailing prompt, or JSON payload");
  return { prompt: text };
}

export async function sessionSendBodyFromRunInput(sessionId: string, args: string[], input: Record<string, unknown>, payload: Record<string, unknown>): Promise<Record<string, unknown>> {
  const fullSendBody = isExplicitSessionSendBody(input);
  const runInput = fullSendBody ? record(input.run ?? input.runBase) : input;
  const runBody = await sessionRunBodyFromArgs(sessionId, args, runInput);
  const runnerDefaults = fullSendBody ? record(input.runnerJob) : {};
  const runnerJob = await sessionRunnerJobBody(args, runnerDefaults);
  const body: Record<string, unknown> = fullSendBody ? { ...input } : {};
  body.run = runBody;
  body.payload = record(input.payload);
  if (Object.keys(record(body.payload)).length === 0) body.payload = payload;
  body.createRunnerJob = agentRunHasFlag(args, "no-runner-job") ? false : input.createRunnerJob !== false;
  body.runnerJob = runnerJob;
  body.dryRun = agentRunHasFlag(args, "dry-run");
  const commandIdempotencyKey = agentRunOption(args, "command-idempotency-key") ?? agentRunOption(args, "idempotency-key");
  if (commandIdempotencyKey) body.commandIdempotencyKey = commandIdempotencyKey;
  return body;
}

export function isExplicitSessionSendBody(input: Record<string, unknown>): boolean {
  return isRecord(input.run)
    || isRecord(input.runBase)
    || isRecord(input.payload)
    || isRecord(input.runnerJob)
    || input.createRunnerJob !== undefined
    || input.commandIdempotencyKey !== undefined;
}

export async function sessionRunBodyFromArgs(sessionId: string, args: string[], input: Record<string, unknown>): Promise<Record<string, unknown>> {
  const existing = await fetchAgentRunSessionOrNull(sessionId, args);
  const sessionPolicy = readAgentRunClientConfig().client.sessionPolicy;
  const policyTarget = resolveAgentRunSessionPolicyTarget();
  const profile = agentRunOption(args, "profile")
    ?? agentRunOption(args, "backend-profile")
    ?? stringOrNull(input.backendProfile)
    ?? stringOrNull(existing?.backendProfile)
    ?? sessionPolicy.backendProfile;
  const body: Record<string, unknown> = { ...input };
  body.tenantId = agentRunOption(args, "tenant-id") ?? stringOrNull(body.tenantId) ?? stringOrNull(existing?.tenantId) ?? sessionPolicy.tenantId;
  body.projectId = agentRunOption(args, "project-id") ?? stringOrNull(body.projectId) ?? stringOrNull(existing?.projectId) ?? sessionPolicy.projectId;
  body.providerId = agentRunOption(args, "provider-id") ?? stringOrNull(body.providerId) ?? stringOrNull(existing?.providerId) ?? defaultAgentRunProviderId(sessionPolicy, policyTarget);
  body.backendProfile = profile;
  body.workspaceRef = jsonObjectOption(args, "workspace-json") ?? record(body.workspaceRef);
  if (Object.keys(record(body.workspaceRef)).length === 0) body.workspaceRef = cloneJsonRecord(sessionPolicy.workspaceRef);
  const executionPolicy = jsonObjectOption(args, "execution-policy-json") ?? record(body.executionPolicy);
  body.executionPolicy = defaultAgentRunExecutionPolicy(profile, sessionPolicy, policyTarget, {
    basePolicy: Object.keys(executionPolicy).length === 0 ? sessionPolicy.executionPolicy : executionPolicy,
  });
  const inheritedSessionRef = existing === null ? {} : {
    conversationId: existing.conversationId,
    threadId: existing.threadId,
    metadata: existing.metadata,
  };
  const sessionRef = { ...inheritedSessionRef, ...record(body.sessionRef) };
  const metadata = { ...record(inheritedSessionRef.metadata), ...record(sessionRef.metadata) };
  const title = agentRunOption(args, "title");
  if (title) metadata.title = title;
  body.sessionRef = { ...sessionRef, sessionId, metadata };
  return body;
}

export function normalizeAipodRenderedQueueTask(task: Record<string, unknown>, args: string[], aipod: string): Record<string, unknown> {
  if (Object.keys(task).length === 0) return task;
  const sessionPolicy = readAgentRunClientConfig().client.sessionPolicy;
  const policyTarget = resolveAgentRunSessionPolicyTarget();
  const explicitProfile = agentRunOption(args, "profile") ?? agentRunOption(args, "backend-profile");
  const renderedProfile = stringOrNull(task.backendProfile);
  const fallbackProfile = sessionPolicy.backendProfile;
  let backendProfile = explicitProfile ?? renderedProfile ?? fallbackProfile;
  if (agentRunProviderCredentialRefs(policyTarget.spec, backendProfile).length === 0) {
    if (explicitProfile !== null) {
      throw new AgentRunRestError("validation-failed", `${policyTarget.configPath} has no providerCredential Secret binding for explicit --backend-profile ${explicitProfile} on ${policyTarget.spec.nodeId}/${policyTarget.spec.lane}`);
    }
    backendProfile = fallbackProfile;
  }
  if (agentRunProviderCredentialRefs(policyTarget.spec, backendProfile).length === 0) {
    throw new AgentRunRestError("validation-failed", `${policyTarget.configPath} has no providerCredential Secret binding for AipodSpec ${aipod} backendProfile=${backendProfile} on ${policyTarget.spec.nodeId}/${policyTarget.spec.lane}`);
  }
  const basePolicy = record(task.executionPolicy);
  const normalized: Record<string, unknown> = { ...task };
  const explicitProviderId = agentRunOption(args, "provider-id");
  normalized.tenantId = stringOrNull(normalized.tenantId) ?? sessionPolicy.tenantId;
  normalized.projectId = stringOrNull(normalized.projectId) ?? sessionPolicy.projectId;
  normalized.providerId = explicitProviderId ?? defaultAgentRunProviderId(sessionPolicy, policyTarget);
  normalized.backendProfile = backendProfile;
  if (!isRecord(normalized.workspaceRef)) normalized.workspaceRef = cloneJsonRecord(sessionPolicy.workspaceRef);
  normalized.executionPolicy = defaultAgentRunExecutionPolicy(backendProfile, sessionPolicy, policyTarget, {
    includeToolCredentials: true,
    basePolicy: Object.keys(basePolicy).length === 0 ? sessionPolicy.executionPolicy : basePolicy,
  });
  return normalized;
}

export function agentRunAipodBindingDisclosure(task: Record<string, unknown>, aipod: string): Record<string, unknown> {
  const policyTarget = resolveAgentRunSessionPolicyTarget();
  const executionPolicy = record(task.executionPolicy);
  const secretScope = record(executionPolicy.secretScope);
  const providerCredentials = arrayRecords(secretScope.providerCredentials).map((credential) => ({
    profile: credential.profile ?? null,
    secretRef: {
      name: record(credential.secretRef).name ?? null,
      keys: Array.isArray(record(credential.secretRef).keys) ? record(credential.secretRef).keys : [],
    },
    valuesPrinted: false,
  }));
  const toolCredentials = arrayRecords(secretScope.toolCredentials).map((credential) => ({
    tool: credential.tool ?? null,
    purpose: credential.purpose ?? null,
    secretRef: {
      name: record(credential.secretRef).name ?? null,
      keys: Array.isArray(record(credential.secretRef).keys) ? record(credential.secretRef).keys : [],
    },
    projection: record(credential.projection),
    valuesPrinted: false,
  }));
  return {
    aipod,
    node: policyTarget.spec.nodeId,
    lane: policyTarget.spec.lane,
    namespace: policyTarget.spec.runtime.namespace,
    policySource: policyTarget.source,
    providerId: task.providerId ?? null,
    backendProfile: task.backendProfile ?? null,
    workspaceRef: task.workspaceRef ?? null,
    executionPolicy: pickCompact(executionPolicy, ["sandbox", "approval", "timeoutMs", "network"]),
    providerCredentials,
    toolCredentials,
    valuesPrinted: false,
  };
}

export function agentRunSessionRunPolicyDisclosure(runBody: Record<string, unknown>): Record<string, unknown> {
  const policyTarget = resolveAgentRunSessionPolicyTarget();
  const executionPolicy = record(runBody.executionPolicy);
  const secretScope = record(executionPolicy.secretScope);
  const providerCredentials = arrayRecords(secretScope.providerCredentials).map((credential) => ({
    profile: credential.profile ?? null,
    secretRef: {
      name: record(credential.secretRef).name ?? null,
      keys: Array.isArray(record(credential.secretRef).keys) ? record(credential.secretRef).keys : [],
    },
    valuesPrinted: false,
  }));
  return {
    node: policyTarget.spec.nodeId,
    lane: policyTarget.spec.lane,
    namespace: policyTarget.spec.runtime.namespace,
    policySource: policyTarget.source,
    providerId: runBody.providerId ?? null,
    backendProfile: runBody.backendProfile ?? null,
    workspaceRef: runBody.workspaceRef ?? null,
    executionPolicy: pickCompact(executionPolicy, ["sandbox", "approval", "timeoutMs", "network"]),
    providerCredentials,
    valuesPrinted: false,
  };
}

export async function sessionSendWithAipodRest(sessionId: string, aipod: string, args: string[]): Promise<Record<string, unknown>> {
  const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, await aipodRenderInputFromArgs(args, 2, { sessionId }));
  const renderedData = record(innerData(rendered));
  const task = normalizeAipodRenderedQueueTask(record(renderedData.queueTask), args, aipod);
  if (Object.keys(task).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask`);
  const sessionRef = record(task.sessionRef);
  const metadata = record(sessionRef.metadata);
  const title = agentRunOption(args, "title") ?? stringOrNull(task.title);
  if (title) metadata.title = title;
  const sessionPolicy = readAgentRunClientConfig().client.sessionPolicy;
  const policyTarget = resolveAgentRunSessionPolicyTarget();
  const backendProfile = stringOrNull(task.backendProfile) ?? sessionPolicy.backendProfile;
  const executionPolicy = record(task.executionPolicy);
  const runBody: Record<string, unknown> = {
    tenantId: stringOrNull(task.tenantId) ?? sessionPolicy.tenantId,
    projectId: stringOrNull(task.projectId) ?? sessionPolicy.projectId,
    providerId: stringOrNull(task.providerId) ?? defaultAgentRunProviderId(sessionPolicy, policyTarget),
    backendProfile,
    workspaceRef: task.workspaceRef ?? cloneJsonRecord(sessionPolicy.workspaceRef),
    sessionRef: { ...sessionRef, sessionId, metadata },
    executionPolicy: Object.keys(executionPolicy).length === 0
      ? defaultAgentRunExecutionPolicy(backendProfile, sessionPolicy, policyTarget, { includeToolCredentials: true })
      : defaultAgentRunExecutionPolicy(backendProfile, sessionPolicy, policyTarget, { includeToolCredentials: true, basePolicy: executionPolicy }),
    resourceBundleRef: task.resourceBundleRef,
    traceSink: { kind: "aipod-session", aipod, sessionId, valuesPrinted: false },
  };
  const runnerDefaults = record(record(renderedData.dispatchDefaults).runnerJob);
  const sendBody: Record<string, unknown> = {
    run: runBody,
    payload: task.payload,
    createRunnerJob: !agentRunHasFlag(args, "no-runner-job"),
    runnerJob: await sessionRunnerJobBody(args, runnerDefaults),
    dryRun: agentRunHasFlag(args, "dry-run"),
  };
  const commandIdempotencyKey = agentRunOption(args, "command-idempotency-key") ?? agentRunOption(args, "idempotency-key");
  if (commandIdempotencyKey) sendBody.commandIdempotencyKey = commandIdempotencyKey;
  const response = await agentRunRestRequest("agentrun sessions send", "POST", `/api/v1/sessions/${encodeURIComponent(sessionId)}/send`, sendBody);
  const data = record(innerData(response));
  return {
    ok: response.ok !== false,
    command: "agentrun sessions send",
    data: {
      ...data,
      aipod,
      profile: String(task.backendProfile ?? ""),
      sessionPolicy: agentRunSessionRunPolicyDisclosure(runBody),
      aipodBinding: agentRunAipodBindingDisclosure(task, aipod),
      valuesPrinted: false,
    },
    bridge: response.bridge,
  };
}

export async function fetchAgentRunSessionOrNull(sessionId: string, args: string[]): Promise<Record<string, unknown> | null> {
  try {
    return record(innerData(await agentRunRestRequest("agentrun sessions show", "GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}${agentRunQuery(args, ["reader-id"])}`)));
  } catch (error) {
    if (error instanceof AgentRunRestError && error.httpStatus === 404) return null;
    throw error;
  }
}

export async function sessionRunnerJobBody(args: string[], defaults: Record<string, unknown> = {}): Promise<Record<string, unknown>> {
  const runnerBody = { ...defaults, ...(await optionalRunnerJsonBody(args)) };
  copyAgentRunOptions(args, runnerBody, ["image", "namespace", "attempt-id", "runner-id", "source-commit", "service-account-name"]);
  const managerUrl = agentRunOption(args, "runner-manager-url");
  if (managerUrl !== null) runnerBody.managerUrl = managerUrl;
  const runnerIdempotencyKey = agentRunOption(args, "runner-idempotency-key");
  if (runnerIdempotencyKey !== null) runnerBody.idempotencyKey = runnerIdempotencyKey;
  return runnerBody;
}

export async function agentRunRestRequest(command: string, method: AgentRunHttpMethod, pathValue: string, body?: unknown): Promise<Record<string, unknown>> {
  const clientConfig = readAgentRunClientConfig();
  if (activeAgentRunRestTarget !== null) return await agentRunLaneRestRequest(command, method, pathValue, body, clientConfig, activeAgentRunRestTarget);
  const auth = resolveAgentRunAuth(clientConfig);
  const bridgeBase = agentRunRestBridgeMetadata(clientConfig, auth, method, pathValue);
  const startedAt = Date.now();
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), clientConfig.manager.timeoutMs);
  let response: Response;
  try {
    const headers: Record<string, string> = {
      [clientConfig.auth.header]: `${clientConfig.auth.scheme} ${auth.value}`,
    };
    const init: RequestInit = { method, headers, signal: controller.signal };
    if (body !== undefined) {
      headers["content-type"] = "application/json";
      init.body = JSON.stringify(body);
    }
    response = await fetch(new URL(pathValue, clientConfig.manager.baseUrl), init);
  } catch (error) {
    const timedOut = isAbortLikeError(error);
    throw new AgentRunRestError(timedOut ? "agentrun-timeout" : "agentrun-connect-failed", timedOut ? `AgentRun server request timed out for ${method} ${pathValue} after ${clientConfig.manager.timeoutMs}ms` : `AgentRun server connection failed for ${method} ${pathValue}: ${error instanceof Error ? error.message : String(error)}`, { bridge: { ...bridgeBase, elapsedMs: Date.now() - startedAt } });
  } finally {
    clearTimeout(timeout);
  }
  const text = await response.text();
  const bridge = { ...bridgeBase, httpStatus: response.status, elapsedMs: Date.now() - startedAt };
  if (text.trim().length === 0) throw new AgentRunRestError("schema-mismatch", `AgentRun server returned an empty response for ${method} ${pathValue}`, { bridge, httpStatus: response.status });
  let envelope: Record<string, unknown>;
  try {
    envelope = record(JSON.parse(text) as unknown);
  } catch {
    throw new AgentRunRestError("schema-mismatch", `AgentRun server returned non-JSON response for ${method} ${pathValue}`, { bridge, httpStatus: response.status });
  }
  if (response.status === 401 || response.status === 403) throw new AgentRunRestError("auth-failed", stringOrNull(envelope.message) ?? "AgentRun API key was rejected", { bridge, httpStatus: response.status, details: safeAgentRunEnvelope(envelope) });
  if (!response.ok) {
    const details = response.status === 404
      ? addAgentRunNotFoundLookupHint(safeAgentRunEnvelope(envelope), clientConfig, method, pathValue, null)
      : safeAgentRunEnvelope(envelope);
    throw new AgentRunRestError(response.status === 404 ? "not-found" : "validation-failed", stringOrNull(envelope.message) ?? `AgentRun request failed with HTTP ${response.status}`, { bridge, httpStatus: response.status, details });
  }
  if (envelope.ok !== true) {
    const failureKind = normalizeAgentRunFailureKind(stringOrNull(envelope.failureKind), response.status);
    throw new AgentRunRestError(failureKind, stringOrNull(envelope.message) ?? `AgentRun request failed for ${method} ${pathValue}`, { bridge, httpStatus: response.status, details: safeAgentRunEnvelope(envelope) });
  }
  return {
    ok: true,
    command,
    data: envelope.data ?? null,
    bridge,
  };
}

export function isAbortLikeError(error: unknown): boolean {
  if (!(error instanceof Error)) return false;
  return error.name === "AbortError" || /abort|timed out|timeout/iu.test(error.message);
}

export async function agentRunLaneRestRequest(command: string, method: AgentRunHttpMethod, pathValue: string, body: unknown, clientConfig: AgentRunClientConfig, target: AgentRunRestTarget): Promise<Record<string, unknown>> {
  const bridgeBase = agentRunLaneRestBridgeMetadata(clientConfig, target, method, pathValue);
  const startedAt = Date.now();
  const proxyBaseUrl = `http://127.0.0.1:${target.spec.runtime.managerPort}`;
  const request = {
    method,
    path: pathValue,
    body: body ?? null,
    baseUrl: proxyBaseUrl,
    serviceBaseUrl: target.spec.runtime.internalBaseUrl,
    timeoutMs: clientConfig.manager.timeoutMs,
    authEnv: clientConfig.auth.env,
    header: clientConfig.auth.header,
    scheme: clientConfig.auth.scheme,
    valuesPrinted: false,
  };
  const captureResult = await capture(target.config, target.spec.nodeKubeRoute, ["sh", "--", agentRunLaneRestProxyScript(target.spec, request)]);
  const captureSummary = compactCapture(captureResult, { full: false, stdoutTailChars: 1200, stderrTailChars: 2000 });
  const bridge = { ...bridgeBase, elapsedMs: Date.now() - startedAt, capture: captureSummary };
  if (captureResult.exitCode !== 0) {
    throw new AgentRunRestError("agentrun-proxy-exec-failed", `AgentRun lane ${target.spec.nodeId}/${target.spec.lane} manager proxy execution failed for ${method} ${pathValue}`, { bridge });
  }
  const proxy = captureJsonPayload(captureResult);
  if (proxy.ok !== true) {
    const proxyFailureKind = stringOrNull(proxy.failureKind);
    const failureKind = classifyAgentRunProxyFailureKind(proxyFailureKind);
    throw new AgentRunRestError(failureKind, stringOrNull(proxy.message) ?? `AgentRun lane ${target.spec.nodeId}/${target.spec.lane} manager proxy failed`, { bridge: { ...bridge, proxy }, details: pickCompact(proxy, ["ok", "failureKind", "message", "elapsedMs", "path", "baseUrl", "valuesPrinted"]) });
  }
  const httpStatus = nonNegativeIntegerOrNull(proxy.httpStatus) ?? 0;
  const text = stringOrNull(proxy.text) ?? "";
  const responseBridge = { ...bridge, httpStatus, proxyElapsedMs: nonNegativeIntegerOrNull(proxy.elapsedMs) };
  if (text.trim().length === 0) throw new AgentRunRestError("schema-mismatch", `AgentRun server returned an empty response for ${method} ${pathValue}`, { bridge: responseBridge, httpStatus });
  let envelope: Record<string, unknown>;
  try {
    envelope = record(JSON.parse(text) as unknown);
  } catch {
    throw new AgentRunRestError("schema-mismatch", `AgentRun server returned non-JSON response for ${method} ${pathValue}`, { bridge: responseBridge, httpStatus });
  }
  if (httpStatus === 401 || httpStatus === 403) throw new AgentRunRestError("auth-failed", stringOrNull(envelope.message) ?? "AgentRun API key was rejected", { bridge: responseBridge, httpStatus, details: safeAgentRunEnvelope(envelope) });
  if (httpStatus < 200 || httpStatus >= 300) {
    const details = httpStatus === 404
      ? addAgentRunNotFoundLookupHint(safeAgentRunEnvelope(envelope), clientConfig, method, pathValue, target)
      : safeAgentRunEnvelope(envelope);
    throw new AgentRunRestError(httpStatus === 404 ? "not-found" : "validation-failed", stringOrNull(envelope.message) ?? `AgentRun request failed with HTTP ${httpStatus}`, { bridge: responseBridge, httpStatus, details });
  }
  if (envelope.ok !== true) {
    const failureKind = normalizeAgentRunFailureKind(stringOrNull(envelope.failureKind), httpStatus);
    throw new AgentRunRestError(failureKind, stringOrNull(envelope.message) ?? `AgentRun request failed for ${method} ${pathValue}`, { bridge: responseBridge, httpStatus, details: safeAgentRunEnvelope(envelope) });
  }
  return {
    ok: true,
    command,
    data: envelope.data ?? null,
    bridge: responseBridge,
  };
}

export function agentRunLaneRestProxyScript(spec: AgentRunLaneSpec, request: Record<string, unknown>): string {
  const requestB64 = Buffer.from(JSON.stringify(request), "utf8").toString("base64");
  const evalScript = String.raw`
const startedAt = Date.now();
let input = {};
let method = "GET";
let path = "/";
let baseUrl = "http://127.0.0.1:8080";
let timeoutMs = 15000;
try {
  input = JSON.parse(Buffer.from(process.env.AGENTRUN_LANE_REST_REQUEST_B64 || "", "base64").toString("utf8"));
  method = String(input.method || "GET");
  path = String(input.path || "/");
  baseUrl = String(input.baseUrl || "http://127.0.0.1:8080");
  timeoutMs = Number(input.timeoutMs || 15000);
  const headers = {};
  const authEnv = String(input.authEnv || "HWLAB_API_KEY");
  const apiKey = process.env[authEnv] || process.env.AGENTRUN_API_KEY || process.env.HWLAB_API_KEY || "";
  if (apiKey && input.header && input.scheme) headers[String(input.header)] = String(input.scheme) + " " + apiKey;
  let body = undefined;
  if (input.body !== null && input.body !== undefined) {
    headers["content-type"] = "application/json";
    body = JSON.stringify(input.body);
  }
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
  let response;
  try {
    response = await fetch(new URL(path, baseUrl).toString(), {
      method,
      headers,
      body,
      signal: controller.signal,
    });
  } finally {
    clearTimeout(timeout);
  }
  const text = await response.text();
  console.log(JSON.stringify({ ok: true, httpStatus: response.status, statusText: response.statusText, text, elapsedMs: Date.now() - startedAt, valuesPrinted: false }));
} catch (error) {
  const name = error instanceof Error ? error.name : "";
  const message = error instanceof Error ? error.message : String(error);
  const timedOut = name === "AbortError" || /abort|timed out|timeout/iu.test(message);
  console.log(JSON.stringify({ ok: false, failureKind: timedOut ? "manager-pod-fetch-timeout" : "manager-pod-fetch-failed", message: timedOut ? "AgentRun manager request timed out for " + method + " " + path + " after " + timeoutMs + "ms" : message, elapsedMs: Date.now() - startedAt, path, baseUrl, valuesPrinted: false }));
}
`;
  return [
    "set -eu",
    `namespace=${shQuote(spec.runtime.namespace)}`,
    `deployment=${shQuote(spec.runtime.managerDeployment)}`,
    `request_b64=${shQuote(requestB64)}`,
    `kubectl -n "$namespace" exec deploy/"$deployment" -- env AGENTRUN_LANE_REST_REQUEST_B64="$request_b64" bun --eval ${shQuote(evalScript)}`,
  ].join("\n");
}

export function resolveAgentRunRestTarget(config: UniDeskConfig | null, options: AgentRunRestTargetOptions): AgentRunRestTarget | null {
  if (options.node === null && options.lane === null) return null;
  if (config === null) throw new AgentRunRestError("validation-failed", "--node/--lane resource queries require UniDesk config");
  const { configPath, spec } = resolveAgentRunLaneTarget(options);
  return { config, configPath, spec };
}

export async function withAgentRunRestTarget<T>(target: AgentRunRestTarget | null, action: () => Promise<T>): Promise<T> {
  const previous = activeAgentRunRestTarget;
  activeAgentRunRestTarget = target;
  try {
    return await action();
  } finally {
    activeAgentRunRestTarget = previous;
  }
}

export function renderAgentRunRestError(command: string, error: AgentRunRestError, options: AgentRunResourceOptions): RenderedCliResult {
  const payload = error.toPayload(command);
  if (options.raw || options.output === "json" || options.output === "yaml") return renderMachine(command, payload, options.output === "yaml" ? "yaml" : "json", false);
  const laneHint = record(record(payload.agentrun).laneAwareLookup);
  const currentEndpoint = record(laneHint.currentEndpoint);
  const nextCommands = Array.isArray(laneHint.nextCommands)
    ? laneHint.nextCommands.filter((value): value is string => typeof value === "string" && value.length > 0).slice(0, 4)
    : [];
  const hintLines = Object.keys(laneHint).length === 0
    ? []
    : [
        "",
        "Lane lookup hint:",
        `  Current baseUrl: ${String(currentEndpoint.baseUrl ?? "(unknown)")}`,
        `  Config: ${String(currentEndpoint.configPath ?? "(unknown)")}`,
        `  ${String(laneHint.summary ?? "The resource was not found on the currently configured AgentRun manager endpoint.")}`,
      ];
  const nextLines = nextCommands.length > 0
    ? nextCommands
    : [
        "Check config/agentrun.yaml manager.baseUrl and the AgentRun API route.",
        "Verify HWLAB_API_KEY is present in env or in the configured auth.file without printing the key.",
      ];
  return renderedCliResult(false, command, [
    `Error: ${payload.failureKind}`,
    String(payload.message ?? ""),
    ...hintLines,
    "",
    "Next:",
    ...nextLines.map((line) => `  ${line}`),
  ].join("\n"));
}

export class AgentRunRestError extends Error {
  readonly failureKind: AgentRunFailureKind;
  readonly bridge: Record<string, unknown> | null;
  readonly httpStatus: number | null;
  readonly details: Record<string, unknown> | null;

  constructor(failureKind: AgentRunFailureKind, message: string, options: { bridge?: Record<string, unknown>; httpStatus?: number; details?: Record<string, unknown> } = {}) {
    super(message);
    this.name = "AgentRunRestError";
    this.failureKind = failureKind;
    this.bridge = options.bridge ?? null;
    this.httpStatus = options.httpStatus ?? null;
    this.details = options.details ?? null;
  }

  toPayload(command: string): Record<string, unknown> {
    const transport = stringOrNull(this.bridge?.mode) ?? "direct-http";
    return {
      ok: false,
      command,
      failureKind: this.failureKind,
      message: this.message,
      transport,
      clientRole: "render-only",
      ...(this.httpStatus === null ? {} : { httpStatus: this.httpStatus }),
      ...(this.bridge === null ? {} : { bridge: this.bridge }),
      ...(this.details === null ? {} : { agentrun: this.details }),
      valuesPrinted: false,
    };
  }
}

export let activeAgentRunRestTarget: AgentRunRestTarget | null = null;
