import { beginOneShotEvidence, oneShotEvidenceRoot } from "./oneshot-evidence.ts";
import { defaultSpawn, type KxmOneShotProcessResult, type KxmOneShotSpawn } from "./oneshot-process.ts";
export { defaultSpawn, type KxmOneShotProcessResult, type KxmOneShotSpawn } from "./oneshot-process.ts";
import {
  BUILTIN_HARNESSES,
  NATIVE_HARNESS_PROVIDERS,
  probeHarnessAssignmentAsync,
  oneShotPermissionArgs,
  type HarnessAssignmentProbeOptions,
  type HarnessStatus,
  type HarnessCatalogEntry,
  type HarnessInventory,
  type OneShotParsedOutput,
} from "./harness.ts";
import {
  calculateModelCost,
  findModelPrice,
  loadPriceCatalog,
  parsePriceCatalog,
  type PriceCatalog,
} from "./prices.ts";
import {
  registerTrustedProducer,
  type KxmProducer,
  type KxmProducerRequest,
  type KxmProducerResult,
} from "./engine.ts";

export interface KxmOneShotProducerOptions {
  projectRoot?: string | undefined;
  evidenceRoot?: string | undefined;
  defaultModel?: string | undefined;
  defaultProvider?: string | undefined;
  inventory?: HarnessInventory | undefined;
  catalog?: readonly HarnessCatalogEntry[] | undefined;
  priceCatalog?: PriceCatalog | undefined;
  env?: NodeJS.ProcessEnv | undefined;
  timeoutMs?: number | undefined;
  spawnProcess?: KxmOneShotSpawn | undefined;
  probeHarness?: ((options: Omit<HarnessAssignmentProbeOptions, "runCommand"> & { signal?: AbortSignal | undefined }) => HarnessStatus | Promise<HarnessStatus>) | undefined;
  resolveModel?: ((agentId: string, runId: string) => {
    provider?: string | undefined;
    model?: string | undefined;
    thinking?: string | undefined;
  } | undefined) | undefined;
}

export interface KxmOneShotProducer extends KxmProducer {
  readonly id: "oneshot";
  produce(request: KxmProducerRequest): Promise<KxmProducerResult>;
  close(): Promise<void>;
}

function isClosingFence(line: string): boolean {
  return /^(`{3,}|~{3,})\s*$/.test(line.trim());
}

// Drop one closing code fence when it is the last line. The caller has already
// trimmed the reply. Any other tail stays, so prose after the object still fails.
function stripOneClosingFence(trimmed: string): string {
  const lineBreak = Math.max(trimmed.lastIndexOf("\n"), trimmed.lastIndexOf("\r"));
  const lastLine = lineBreak < 0 ? trimmed : trimmed.slice(lineBreak + 1);
  if (!isClosingFence(lastLine)) return trimmed;
  if (lineBreak < 0) return "";
  return trimmed.slice(0, lineBreak).trimEnd();
}

// How many `}` characters, counting from the end of the reply, we will try
// as the end of an outcome object.
const MAX_OUTCOME_CLOSERS = 32;

// How many `{` openers before that closer we will consider, nearest last.
const MAX_OUTCOME_OPENERS = 32;

// A candidate whose UTF-8 size is over this is not parsed. Older openers
// for the same closer contain the nearer slice, so the opener walk stops
// there. The count is Buffer.byteLength, not JavaScript string length.
const MAX_OUTCOME_SLICE_BYTES = 256 * 1024;

// Total UTF-8 bytes of candidate slices JSON.parse may see in one lookup.
// Crossing this budget stops the lookup. Same byte measure as the slice cap.
const MAX_OUTCOME_PARSE_BYTES = 1024 * 1024;

// How many `{` / `[` openers at the end of the prefix are re-lexed. A `"`
// is not an opener. Each re-lex walks the body from that opener to the end,
// so the lexer work is linear in the body per opener checked, per candidate.
// This cap only limits how many openers are re-lexed. It does not bound the
// lexer. The slice budgets bound JSON.parse bytes, not these scans. If the
// walk reaches the cap and another container opener is still earlier in the
// prefix, the prefix is ambiguous and the candidate is rejected.
const MAX_PREFIX_CONTAINER_OPENERS = 16;

// Naive prefix scan. Quote handling toggles on every unescaped `"`. Braces
// and brackets count only outside a string. Extra closers stay at depth 0
// because they are prose, not an unclosed container. The slice itself is
// not scanned: JSON.parse owns strings and escapes there. O(prefix).
function outcomePrefixIsAnchored(prefix: string): boolean {
  let objectDepth = 0;
  let arrayDepth = 0;
  let inString = false;
  let escape = false;
  for (let i = 0; i < prefix.length; i++) {
    const ch = prefix[i];
    if (inString) {
      if (escape) {
        escape = false;
        continue;
      }
      if (ch === "\\") {
        escape = true;
        continue;
      }
      if (ch === "\"") inString = false;
      continue;
    }
    if (ch === "\"") {
      inString = true;
      continue;
    }
    if (ch === "{") objectDepth += 1;
    else if (ch === "}" && objectDepth > 0) objectDepth -= 1;
    else if (ch === "[") arrayDepth += 1;
    else if (ch === "]" && arrayDepth > 0) arrayDepth -= 1;
  }
  return objectDepth === 0 && arrayDepth === 0 && !inString && !escape;
}

// Same string, escape, and depth machine as the prefix scan, started at a
// `{` or `[`. A closer that does not match an opener this scan pushed is
// invalid. An invalid scan is not truncation. Truncation is a valid scan
// that reaches the end of the body still inside a container or a string.
// One call walks from `start` through the rest of the body. The walk is
// linear in that span. No byte budget stops it.
function containerLexIsTruncated(body: string, start: number): boolean {
  const stack: string[] = [];
  let inString = false;
  let escape = false;
  for (let i = start; i < body.length; i++) {
    const ch = body[i];
    if (inString) {
      if (escape) {
        escape = false;
        continue;
      }
      if (ch === "\\") {
        escape = true;
        continue;
      }
      if (ch === "\"") inString = false;
      continue;
    }
    if (ch === "\"") {
      inString = true;
      continue;
    }
    if (ch === "{" || ch === "[") {
      stack.push(ch);
      continue;
    }
    if (ch === "}" || ch === "]") {
      const open = ch === "}" ? "{" : "[";
      const top = stack[stack.length - 1];
      if (top !== open) return false;
      stack.pop();
    }
  }
  return stack.length > 0 || inString || escape;
}

// True when one of the last container openers in the prefix lexes as a
// value that is still open at the end of the body, or when more container
// openers sit further back than the re-lex cap. Those further openers are
// not scanned. The prefix is ambiguous, so the candidate fails closed.
// Quote positions are left to the naive machine.
function prefixHidesTruncatedContainer(body: string, openAt: number): boolean {
  let seen = 0;
  for (let i = openAt - 1; i >= 0; i--) {
    const ch = body[i];
    if (ch !== "{" && ch !== "[") continue;
    if (seen >= MAX_PREFIX_CONTAINER_OPENERS) return true;
    seen++;
    if (containerLexIsTruncated(body, i)) return true;
  }
  return false;
}

// The outcome object is a JSON object that ends the trimmed reply. One
// closing code fence may follow it; the fence is removed before the walk.
// Candidates are slices from a `{` to a recent `}`, nearest last. Only the
// last 32 closers are visited, and only a closer on the reply's last
// character can end the reply. For that closer the last 32 openers are
// tried. A slice whose UTF-8 size is over 256 KiB is skipped. Parsed slices
// share a 1 MiB UTF-8 budget, and crossing it stops the lookup. Both limits
// use Buffer.byteLength. JSON.parse is the only
// string and escape authority inside the slice. A slice that parses to a
// plain object whose outcome is a string in allowedOutcomes is a candidate.
// The prefix before that opener, with trailing whitespace removed, must end
// at depth 0 for both braces and arrays, outside a string, with no dangling
// escape. Any other end state is truncated or ambiguous, so the candidate
// is rejected and the next one is tried. The naive machine plus the
// container-lexer guard reject truncation. A prefix with more container
// openers than the re-lex cap is ambiguous and fails closed. Prose with an
// odd quote count fails closed. An accepted candidate ends the lookup.
// Nothing left is failed: prose after the object, no object, a disallowed
// outcome, an inner object at the end of a truncated outer object or array,
// an object inside an unclosed string, an ambiguous prefix past the re-lex
// cap, or a budget exhausted before a candidate.
function determineOutcome(text: string, allowedOutcomes: readonly string[]): string {
  const trimmed = text.trim();
  if (!trimmed) return "failed";
  const body = stripOneClosingFence(trimmed);
  if (!body) return "failed";
  let seenClosers = 0;
  let parseBytes = 0;
  for (let closeAt = body.length - 1; closeAt >= 0 && seenClosers < MAX_OUTCOME_CLOSERS; closeAt--) {
    if (body[closeAt] !== "}") continue;
    seenClosers++;
    const end = closeAt + 1;
    // A candidate has to end the reply. One closer can sit at that position.
    if (end !== body.length) continue;
    let seenOpeners = 0;
    for (let openAt = closeAt - 1; openAt >= 0 && seenOpeners < MAX_OUTCOME_OPENERS; openAt--) {
      if (body[openAt] !== "{") continue;
      seenOpeners++;
      const slice = body.slice(openAt, end);
      const sliceBytes = Buffer.byteLength(slice, "utf8");
      if (sliceBytes > MAX_OUTCOME_SLICE_BYTES) break;
      if (parseBytes + sliceBytes > MAX_OUTCOME_PARSE_BYTES) return "failed";
      parseBytes += sliceBytes;
      let result: unknown;
      try {
        result = JSON.parse(slice);
      } catch {
        continue;
      }
      if (!result || typeof result !== "object" || Array.isArray(result)) continue;
      const outcome = (result as Record<string, unknown>).outcome;
      if (typeof outcome !== "string" || !allowedOutcomes.includes(outcome)) continue;
      const prefix = body.slice(0, openAt).trimEnd();
      if (!outcomePrefixIsAnchored(prefix)) continue;
      if (prefixHidesTruncatedContainer(body, openAt)) continue;
      return outcome;
    }
  }
  return "failed";
}

export function createKxmOneShotProducer(options: KxmOneShotProducerOptions = {}): KxmOneShotProducer {
  const running = new Map<AbortController, Promise<KxmProducerResult>>();
  let closed = false;

  function requireHarness(request: KxmProducerRequest): string {
    if (request.harness) return request.harness;
    throw new Error("producer_harness_required");
  }

  function parseModelString(spec: string, harness: string): { provider: string; model: string } {
    const trimmed = spec.trim();
    if (trimmed.includes("/")) {
      const idx = trimmed.indexOf("/");
      return { provider: trimmed.slice(0, idx).toLowerCase(), model: trimmed.slice(idx + 1) };
    }
    const fallbackProvider = NATIVE_HARNESS_PROVIDERS[harness] ?? options.defaultProvider ?? "anthropic";
    return { provider: fallbackProvider, model: trimmed };
  }

  function resolveModelForRequest(
    request: KxmProducerRequest,
    harness: string,
  ): { provider: string; model: string; thinking?: string | undefined } {
    if (request.model) {
      // A separate provider field means the model id is bare and may itself be
      // namespaced (openrouter + qwen/qwen3.7-plus); reparsing it as a
      // provider/model selector strips the namespace — the same defect #262 fixed
      // in the pi producer. Only parse when the provider must come from the string.
      if (request.provider) {
        return { provider: request.provider.toLowerCase(), model: request.model, thinking: request.thinking };
      }
      const parsed = parseModelString(request.model, harness);
      return {
        provider: request.provider?.toLowerCase() ?? parsed.provider,
        model: parsed.model,
        thinking: request.thinking,
      };
    }
    if (options.resolveModel) {
      const resolved = options.resolveModel(request.agentId, request.runId);
      if (resolved && resolved.model) {
        if (resolved.provider) {
          return { provider: resolved.provider.toLowerCase(), model: resolved.model, thinking: resolved.thinking };
        }
        const parsed = parseModelString(resolved.model, harness);
        return {
          provider: resolved.provider?.toLowerCase() ?? parsed.provider,
          model: parsed.model,
          thinking: resolved.thinking,
        };
      }
    }
    if (options.defaultModel) {
      const parsed = parseModelString(options.defaultModel, harness);
      return { provider: parsed.provider, model: parsed.model, thinking: request.thinking };
    }
    throw new Error("producer_route_not_admitted");
  }

  async function checkAuth(harness: string, provider: string, model: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<HarnessStatus> {
    if (options.inventory) {
      const entry = options.inventory.harnesses.find((h) => h.id === harness);
      if (!entry || !entry.detected || entry.authenticated !== true) {
        throw new Error(`${harness}_not_authenticated: ${harness} harness not authenticated in inventory`);
      }
    }

    const probeFn = options.probeHarness ?? probeHarnessAssignmentAsync;
    const probe = await probeFn({ harness, provider, model, env, signal, timeoutMs: 10_000 });

    if (!probe.detected) {
      throw new Error(`${harness}_not_authenticated: ${harness} harness not detected (${probe.issues.join(", ")})`);
    }
    if (probe.authenticated !== true) {
      throw new Error(`${harness}_not_authenticated: ${harness} not authenticated (${probe.issues.join(", ")})`);
    }
    return probe;
  }

  async function executeRequest(request: KxmProducerRequest): Promise<KxmProducerResult> {
      const startTime = Date.now();
      const harness = requireHarness(request);
      const resolved = resolveModelForRequest(request, harness);

      if (closed) throw new Error("oneshot_producer_closed");
      const cancelled = (): KxmProducerResult => ({ outcome: "cancelled", costBasis: "unknown", costUsd: null,
        latencyMs: Math.max(0, Date.now() - startTime), harness, provider: resolved.provider,
        requestedModel: resolved.model, effectiveModel: "unknown", agentRole: request.agentRole ?? request.agentId });
      if (request.signal.aborted) return cancelled();
      const catalogEntry = (options.catalog ?? BUILTIN_HARNESSES).find((h) => h.id === harness);
      if (!catalogEntry?.oneShot) throw new Error(`oneshot_harness_unsupported: ${harness}`);
      const permission = request.permission === "edit" || request.contextPacket?.task.permissionCeiling === "edit"
        ? "edit"
        : "read-only";
      const permissionArgs = oneShotPermissionArgs(harness, permission);
      if (!permissionArgs) {
        throw new Error(`oneshot_harness_unsupported: ${harness} ${permission === "edit" ? "writer_profile_unaudited" : "permission_profile_unaudited"}`);
      }
      // Pin the environment for auth and execution; don't observe subscription
      // auth under one environment and then spawn under changed API-key settings.
      const env = { ...(options.env ?? process.env) };
      let auth: HarnessStatus;
      try { auth = await checkAuth(harness, resolved.provider, resolved.model, env, request.signal); }
      catch (error) { if (request.signal.aborted) return cancelled(); throw error; }
      if (request.signal.aborted) return cancelled();

      const command = auth.command ?? catalogEntry.commands[0] ?? harness;
      const oneShot = catalogEntry.oneShot;

      let args: string[];
      if (harness === "claude") {
        args = [
          "-p",
          "--model",
          resolved.model,
          ...(resolved.thinking ? ["--effort", resolved.thinking] : []),
          ...permissionArgs,
          "--output-format",
          "json",
        ];
      } else if (harness === "codex") {
        args = [
          "exec",
          "-m",
          resolved.model,
          ...(resolved.thinking ? ["-c", `model_reasoning_effort="${resolved.thinking}"`] : []),
          ...permissionArgs,
          "--json",
          "-",
        ];
      } else if (harness === "grok") {
        args = ["--model", resolved.model,
          ...(resolved.thinking ? ["--reasoning-effort", resolved.thinking] : []),
          ...permissionArgs, "--output-format", "json"];
      } else if (harness === "agy") {
        args = [
          "-m",
          resolved.model,
          ...permissionArgs,
          "--output-format",
          "json",
          "-p",
        ];
      } else if (harness === "kimi") {
        args = [
          "-m",
          resolved.model,
          ...permissionArgs,
          "--output-format",
          "stream-json",
          "-p",
        ];
      } else if (harness === "pi") {
        // pi's model flag takes the provider-qualified id — exactly the auth probe's
        // rule (#265): prefix with the provider unless the model already starts with it.
        // A bare "contains a slash" test is wrong for provider-local namespaces
        // (openrouter + qwen/qwen3.7-plus must spawn as the full three-part id).
        const qualified = resolved.provider && resolved.model.startsWith(`${resolved.provider}/`)
          ? resolved.model
          : resolved.provider ? `${resolved.provider}/${resolved.model}` : resolved.model;
        args = [
          "--model",
          qualified,
          ...permissionArgs,
          ...oneShot.argv,
        ];
      } else {
        throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
      }

      const taskPrompt = request.prompt
        ?? `Execute step ${request.stepId} (attempt ${request.stepAttempt}) for agent ${request.agentId}.`;
      const promptMessage = `${taskPrompt}\n\nReturn a final JSON object with an "outcome" field chosen from ${JSON.stringify(request.allowedOutcomes)} and a concise "summary". Do not wrap it in Markdown. If no allowed outcome is truthful, return {"outcome":"failed"}; never infer success from an incomplete task.`;

      let input: string | undefined;
      if (oneShot.promptVia === "stdin") {
        input = promptMessage;
      } else {
        if (harness === "grok") args.push("--single");
        args.push(promptMessage);
      }

      const spawnFn = options.spawnProcess ?? defaultSpawn;
      const cwd = options.projectRoot ?? process.cwd();
      const timeoutMs = request.timeoutMs ?? options.timeoutMs;
      const evidence = await beginOneShotEvidence(options.evidenceRoot ?? oneShotEvidenceRoot(), {
        runId: request.runId, stepId: request.stepId, attemptId: request.attemptId, assignmentId: request.assignmentId,
        harness, provider: resolved.provider, model: resolved.model, cwd, command, args, input,
        timeoutMs: timeoutMs ?? 120_000,
      }, [request.capability, ...Object.entries(env).filter(([key]) => /TOKEN|KEY|SECRET|PASSWORD|COOKIE|AUTH/i.test(key)).map(([, value]) => value ?? "")]);
      let procResult: KxmOneShotProcessResult;
      try {
        procResult = request.signal.aborted
          ? { stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") }
          : await spawnFn(command, args, { cwd, env, input, timeoutMs, signal: request.signal });
      } catch (error) {
        // A rejected adapter promise is not proof that no process/effect started.
        procResult = { stdout: "", stderr: "", code: null, observedChildExit: false, error: error instanceof Error ? error : new Error("process_adapter_error") };
      }

      // Parse available usage even when execution failed or was interrupted.
      let parsed: OneShotParsedOutput;
      try { parsed = oneShot.usageParser(procResult.stdout, procResult.stderr, resolved.model); }
      catch { parsed = { text: "", isError: true }; }
      const aborted = request.signal.aborted || procResult.error?.message === "process_aborted";
      // Process groups are cleanup, not containment. A detached descendant or
      // provider leader may outlive the client; interrupted effects need recovery.
      const unverifiedDescendants = procResult.unverifiedDescendants === true
        || procResult.terminationRequested === true
        || procResult.error?.message === "process_exit_unobserved"
        || (procResult.observedChildExit === false && procResult.started !== false);
      const effectUncertain = unverifiedDescendants || Boolean(procResult.signal);
      const transportFailed = procResult.code !== 0 || Boolean(procResult.error) || effectUncertain;
      const outcome = aborted ? "cancelled" : transportFailed || parsed.isError ? "failed"
        : determineOutcome(parsed.text, request.allowedOutcomes);
      const providerMetadata: Record<string, string | number | boolean> = {
        processStatus: aborted ? "aborted" : transportFailed ? "failed" : "completed",
        executionEvidenceId: evidence.id,
        permission,
        permissionProfile: permission === "edit" ? "writer" : "read-only",
        authored: false,
      };
      if (procResult.code !== null && Number.isFinite(procResult.code)) providerMetadata.processExitCode = procResult.code;
      if (procResult.signal) providerMetadata.processSignal = procResult.signal;
      if (procResult.observedChildExit !== undefined) providerMetadata.observedChildExit = procResult.observedChildExit;
      if (procResult.started !== undefined) providerMetadata.processStarted = procResult.started;
      if (procResult.terminationRequested !== undefined) providerMetadata.terminationRequested = procResult.terminationRequested;
      if (unverifiedDescendants) providerMetadata.descendantEffects = "unverified";
      if (procResult.error) {
        const reason = procResult.error.message;
        providerMetadata.processError = /^process_(aborted|timeout|output_limit|stdin_error|stdio_error|stdio_unclosed|exit_unobserved)$/.test(reason) ? reason : "process_error";
      }
      const latencyMs = Math.max(0, Date.now() - startTime);
      const usage = parsed.usage;
      const validCount = (value: unknown): value is number => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
      const boundedCount = (field: string, value: unknown): number | null => {
        if (validCount(value) && value <= 1_000_000) return value;
        if (typeof value === "number" && Number.isFinite(value)) {
          providerMetadata[`raw${field[0]!.toUpperCase()}${field.slice(1)}`] = value;
        }
        return null;
      };
      const tokensIn = boundedCount("tokensIn", usage?.tokensIn);
      const tokensOut = boundedCount("tokensOut", usage?.tokensOut);
      const cacheReadTokens = boundedCount("cacheReadTokens", usage?.cacheReadTokens);
      const cacheWriteTokens = boundedCount("cacheWriteTokens", usage?.cacheWriteTokens);
      const contextTokens = boundedCount("contextTokens", usage?.contextTokens);
      if (typeof usage?.costUsd === "number" && Number.isFinite(usage.costUsd) && usage.costUsd >= 0) providerMetadata.providerReportedCostUsd = usage.costUsd;

      // List estimates and provider-reported amounts are not proof of a bill.
      let catalog: PriceCatalog | undefined;
      try {
        catalog = options.priceCatalog ? parsePriceCatalog(JSON.stringify(options.priceCatalog))
          : loadPriceCatalog(options.projectRoot ?? process.cwd());
      } catch { providerMetadata.priceCatalogUnavailable = true; }
      // Without a freshness-aware vendor feed, don't silently quote an older
      // local snapshot. Even today's hash-verified snapshot is only an estimate.
      if (catalog && catalog.date !== new Date().toISOString().slice(0, 10)) {
        providerMetadata.priceCatalogStale = true;
        catalog = undefined;
      }
      const subscription = ["claude.ai", "ChatGPT", "antigravity-oauth"].includes(auth.authMethod ?? "");
      const costBasis = subscription ? "unmetered" as const : "unknown" as const;
      const costUsd = null;
      const priceRef = subscription ? `subscription:${harness}` : undefined;
      if (auth.authMethod) providerMetadata.authMethod = auth.authMethod;
      const priceRow = catalog ? findModelPrice(catalog, resolved.model, resolved.provider) : undefined;
      const flatTier = priceRow?.tiers.length === 1 && priceRow.tiers[0]?.upToContextTokens == null ? priceRow.tiers[0] : undefined;
      // Unknown context occupancy cannot select a context tier. Claude reports
      // uncached input separately; other protocols need their own price semantics.
      if (catalog && harness === "claude" && flatTier
        && validCount(usage?.tokensIn) && validCount(usage?.tokensOut)
        && validCount(usage?.cacheReadTokens) && validCount(usage?.cacheWriteTokens)
        && (usage.cacheReadTokens === 0 || flatTier.cacheReadPerMillion != null)
        && (usage.cacheWriteTokens === 0 || flatTier.cacheWritePerMillion != null)) {
        const calculated = calculateModelCost(catalog, {
          model: resolved.model, provider: resolved.provider,
          tokensIn: usage.tokensIn, tokensOut: usage.tokensOut,
          cacheReadTokens: usage.cacheReadTokens, cacheWriteTokens: usage.cacheWriteTokens,
          contextTokens,
        });
        if (calculated && Number.isFinite(calculated.costUsd)) {
          providerMetadata.listCostUsd = calculated.costUsd;
          providerMetadata.listPriceRef = calculated.priceRef;
          providerMetadata.listPriceSha256 = catalog.sha256;
        }
      }

      const result: KxmProducerResult = {
        outcome,
        costBasis,
        costUsd,
        tokensIn,
        tokensOut,
        cacheReadTokens,
        cacheWriteTokens,
        contextTokens,
        latencyMs,
        harness,
        provider: resolved.provider,
        requestedModel: resolved.model,
        effectiveModel: parsed.effectiveModel ?? "unknown",
        providerMetadata,
        ...(effectUncertain ? { effectUncertain: true as const } : {}),
        ...(resolved.thinking !== undefined ? { thinking: resolved.thinking } : {}),
        agentRole: request.agentRole ?? request.agentId,
        ...(priceRef !== undefined ? { priceRef } : {}),
      };
      try { providerMetadata.executionEvidenceSha256 = await evidence.finish(procResult, result); }
      catch {
        providerMetadata.evidenceWriteFailed = true;
        return { ...result, outcome: "failed", effectUncertain: true };
      }
      return result;
  }

  const producer: KxmOneShotProducer = {
    id: "oneshot",
    produce(request): Promise<KxmProducerResult> {
      const controller = new AbortController();
      const signal = AbortSignal.any([request.signal, controller.signal]);
      const pending = Promise.resolve().then(() => executeRequest({ ...request, signal }))
        .finally(() => { running.delete(controller); });
      running.set(controller, pending);
      return pending;
    },

    async close(): Promise<void> {
      closed = true;
      const pending = [...running.values()];
      for (const controller of running.keys()) controller.abort();
      await Promise.allSettled(pending);
    },
  };

  registerTrustedProducer(producer);
  return producer;
}
