import {
  FileAgentCandidateExecutionClaimStore,
  buildAgentCandidateBundle,
  captureAgentCandidateWorkspace,
  captureAgentCandidateWorkspaceFiles,
  createAgentCandidateWorkspacePort,
  createProtectedAgentCandidateModelPort,
  disposePreparedAgentCandidateExecution,
  recoverExpiredAgentCandidateExecution
} from "./chunk-FTXM7Q7P.js";
import {
  mcpToolsForRuntimeMcp,
  mcpToolsForRuntimeMcpSubset
} from "./chunk-MSUWXCHD.js";
import {
  DEFAULT_ROUTER_BASE_URL,
  cleanModelId,
  getModels,
  resolveChatModel,
  resolveRouterBaseUrl,
  validateChatModelId
} from "./chunk-NURIQBJQ.js";
import {
  DELEGATED_LOOP_MODES,
  auditLoopRunner,
  isDelegatedLoopMode,
  parseLoopRunnerArgv,
  researchLoopRunner,
  runDelegatedLoop,
  runLoopRunnerCli,
  selfImproveLoopRunner,
  worktreeLoopRunner
} from "./chunk-KYPVUEJ4.js";
import "./chunk-SGKPNBXE.js";
import {
  applyImprovementWinnerToProfile,
  improve,
  improvementDriver,
  rawTraceDistiller
} from "./chunk-7NCP74SL.js";
import {
  CANDIDATE_TRACE_ENV,
  CANDIDATE_TRACE_TAGS,
  InMemoryAgentCandidateExecutionClaimStore,
  applyExactAgentProfileDiff,
  candidateExecutionClaim,
  canonicalCandidateDigest,
  executePreparedAgentCandidate,
  parseExactAgentProfile,
  parseExactAgentProfileDiff,
  persistCandidateOutputArtifact,
  prepareAgentCandidateExecution,
  sealAgentCandidateBundle,
  verifyAgentCandidateBundle
} from "./chunk-37V4KQDC.js";
import {
  InMemoryRuntimeSessionStore,
  createIterableBackend,
  createOpenAICompatibleBackend,
  createSandboxPromptBackend,
  newRuntimeSession,
  normalizeBackendStreamEvent,
  nowIso,
  touchSession
} from "./chunk-7HH22XN4.js";
import "./chunk-QDSOD7RC.js";
import {
  mcpBuildPrompt,
  mcpServeVerifier,
  toolBuildPrompt
} from "./chunk-6O73TRHW.js";
import {
  AGENTIC_PROFILE_RESOURCE_ROOT,
  agenticGenerator,
  commandVerifier
} from "./chunk-GHXW56PO.js";
import {
  RESEARCH_SUPERVISOR_SYSTEM_PROMPT,
  createAgentKnowledgeReadinessCheck,
  createSupervisedKnowledgeUpdater,
  formatSupervisedKnowledgeTask,
  knowledgeReadinessDeliverable,
  runKnowledgeImprovementJob,
  runSupervisedKnowledgeUpdate
} from "./chunk-TNUBPPZA.js";
import "./chunk-5QOB7H74.js";
import {
  composeRuntimeHooks,
  defineRuntimeHooks,
  notifyRuntimeDecisionPoint,
  notifyRuntimeHookEvent
} from "./chunk-FRXDOBFP.js";
import "./chunk-DPEUKJRO.js";
import {
  INTELLIGENCE_WIRE_VERSION,
  buildLoopOtelSpans,
  buildLoopSpanNodes,
  buildRuntimeEventOtelSpans,
  createOtelExporter,
  createRuntimeEventCollector,
  createRuntimeStreamEventCollector,
  exportEvalRuns,
  loopEventToOtelSpan,
  sanitizeAgentRuntimeEvent,
  sanitizeKnowledgeReadinessReport,
  sanitizeRuntimeStreamEvent
} from "./chunk-ISTDY47H.js";
import "./chunk-WIPGQ4GT.js";
import "./chunk-VSWBYWFK.js";
import "./chunk-3MDZX7YU.js";
import "./chunk-M22PL4GH.js";
import "./chunk-EP6RVHMX.js";
import {
  AgentEvalError,
  BackendTransportError,
  ConfigError,
  JudgeError,
  NotFoundError,
  PlannerError,
  RuntimeRunStateError,
  SessionMismatchError,
  ValidationError
} from "./chunk-YEJR7IXO.js";
import "./chunk-DGUM43GV.js";

// src/conversation/call-policy.ts
var CircuitOpenError = class extends Error {
  constructor(participant, retryAfterMs) {
    super(
      `circuit open for participant '${participant}'; ${retryAfterMs}ms remaining before retry allowed`
    );
    this.name = "CircuitOpenError";
  }
};
var DeadlineExceededError = class extends Error {
  constructor(deadlineMs) {
    super(`backend call exceeded per-attempt deadline of ${deadlineMs}ms`);
    this.name = "DeadlineExceededError";
  }
};
var defaultIsRetryable = (err) => {
  if (err instanceof DeadlineExceededError) return true;
  if (err instanceof Error) {
    const name = err.name;
    const message = err.message.toLowerCase();
    if (name === "AbortError" || name === "TimeoutError") return true;
    if (message.includes("econnreset") || message.includes("etimedout") || message.includes("econnrefused") || message.includes("socket hang up") || message.includes("network") || message.includes("fetch failed")) {
      return true;
    }
  }
  return false;
};
var CircuitBreakerState = class {
  constructor(config) {
    this.config = config;
  }
  config;
  consecutiveFailures = 0;
  openedAt;
  /**
   * Check whether the next call is allowed. Throws `CircuitOpenError` when
   * the breaker is open and the cooldown hasn't elapsed.
   */
  preflight(participant, now = Date.now()) {
    if (!this.config || this.openedAt === void 0) return;
    const remaining = this.config.cooldownMs - (now - this.openedAt);
    if (remaining > 0) {
      throw new CircuitOpenError(participant, remaining);
    }
    this.openedAt = void 0;
    this.consecutiveFailures = 0;
  }
  recordSuccess() {
    this.consecutiveFailures = 0;
    this.openedAt = void 0;
  }
  recordFailure(now = Date.now()) {
    if (!this.config) return;
    this.consecutiveFailures += 1;
    if (this.consecutiveFailures >= this.config.failuresToOpen) {
      this.openedAt = now;
    }
  }
};
function makePerAttemptSignal(parentSignal, deadlineMs) {
  const controller = new AbortController();
  let deadlineError;
  const cleanups = [];
  if (parentSignal) {
    if (parentSignal.aborted) controller.abort(parentSignal.reason);
    else {
      const onAbort = () => controller.abort(parentSignal.reason);
      parentSignal.addEventListener("abort", onAbort, { once: true });
      cleanups.push(() => parentSignal.removeEventListener("abort", onAbort));
    }
  }
  if (deadlineMs !== void 0) {
    const ms = deadlineMs;
    const timer = setTimeout(() => {
      deadlineError = new DeadlineExceededError(ms);
      controller.abort(deadlineError);
    }, ms);
    cleanups.push(() => clearTimeout(timer));
  }
  return {
    signal: controller.signal,
    dispose() {
      for (const c of cleanups) c();
    },
    getDeadlineError() {
      return deadlineError;
    }
  };
}
function computeBackoff(spec, attempt) {
  if (spec === void 0) {
    const base = 250;
    const jitter = Math.floor(Math.random() * base);
    return base * 2 ** (attempt - 1) + jitter;
  }
  if (typeof spec === "function") return Math.max(0, spec(attempt));
  return Math.max(0, spec);
}
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// src/conversation/headers.ts
var FORWARD_HEADERS = {
  /** Forwarded original-user identity (`Bearer sk-tan-<user>`); downstream gateways bill against this. */
  authorization: "x-tangle-forwarded-authorization",
  /** Monotonically incremented on every gateway hop. Refused at MAX_DEPTH. */
  depth: "x-tangle-forwarded-depth",
  /** Top-level conversation run identifier, propagated through every nested call. */
  runId: "x-tangle-runid",
  /** This call's turn within the run; deterministic + stable across retries. */
  turnId: "x-tangle-turnid",
  /** When the call is *inside* another turn (recursion), the parent turn's id. */
  parentTurnId: "x-tangle-parent-turnid",
  /** Logical conversation peer label at the sending side, for trace stitching. */
  speaker: "x-tangle-speaker"
};
var DEFAULT_MAX_DEPTH = 4;
function lc(name) {
  return name.toLowerCase();
}
function readDepth(headers) {
  const raw = pickHeader(headers, FORWARD_HEADERS.depth);
  if (raw === void 0 || raw === "") return 0;
  const n = Number(raw);
  if (!Number.isInteger(n) || n < 0) {
    throw new Error(
      `invalid ${FORWARD_HEADERS.depth} header value '${raw}' \u2014 must be a non-negative integer`
    );
  }
  return n;
}
function isDepthExceeded(inboundDepth, max = DEFAULT_MAX_DEPTH) {
  return inboundDepth >= max;
}
function buildForwardHeaders(input) {
  const out = {
    [FORWARD_HEADERS.depth]: String(input.inboundDepth + 1),
    [FORWARD_HEADERS.runId]: input.runId,
    [FORWARD_HEADERS.turnId]: input.turnId,
    [FORWARD_HEADERS.speaker]: input.speaker
  };
  if (input.forwardedAuthorization !== void 0) {
    out[FORWARD_HEADERS.authorization] = input.forwardedAuthorization;
  }
  if (input.parentTurnId !== void 0) {
    out[FORWARD_HEADERS.parentTurnId] = input.parentTurnId;
  }
  return out;
}
function pickHeader(headers, name) {
  const target = lc(name);
  for (const key of Object.keys(headers)) {
    if (lc(key) === target) {
      const value = headers[key];
      if (Array.isArray(value)) return value[0];
      return value;
    }
  }
  return void 0;
}

// src/conversation/turn-id.ts
function turnId(runId, index, speaker) {
  return `${runId}.t${index}.${slugifySpeaker(speaker)}`;
}
function slugifySpeaker(speaker) {
  const cleaned = speaker.normalize("NFKD").replace(/[^\w-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
  return cleaned || "anon";
}

// src/conversation/run-conversation.ts
async function runConversation(conversation, options) {
  let result;
  for await (const event of runConversationStream(conversation, options)) {
    if (options.onEvent) await options.onEvent(event);
    if (event.type === "conversation_end") result = event.result;
  }
  if (!result) {
    throw new BackendTransportError(
      "conversation",
      "conversation stream ended without a conversation_end event"
    );
  }
  return result;
}
async function* runConversationStream(conversation, options) {
  const runId = options.runId ?? `conv_${crypto.randomUUID()}`;
  const inboundDepth = options.inboundDepth ?? 0;
  const callerHeaders = options.propagatedHeaders ?? {};
  const forwardedAuthorization = callerHeaders[FORWARD_HEADERS.authorization];
  const breakers = /* @__PURE__ */ new Map();
  for (const participant of conversation.participants) {
    const cfg = participant.callPolicy?.circuitBreaker ?? conversation.policy.defaultCallPolicy?.circuitBreaker;
    breakers.set(participant.name, new CircuitBreakerState(cfg));
  }
  let transcript = [];
  let spentCreditsCents = 0;
  let startedAt = nowIso();
  let resumed = false;
  if (options.journal) {
    const prior = await options.journal.loadRun(runId);
    if (prior) {
      if (prior.halted) {
        const replayResult = {
          runId,
          transcript: prior.turns,
          turns: prior.turns.length,
          spentCreditsCents: prior.turns.reduce(
            (sum, t) => sum + centsFromUsd(t.usage?.costUsd ?? 0),
            0
          ),
          halted: prior.halted,
          durationMs: 0,
          startedAt: prior.startedAt,
          endedAt: prior.endedAt ?? prior.startedAt
        };
        yield {
          type: "conversation_resumed",
          runId,
          participants: conversation.participants.map((p) => p.name),
          transcript: prior.turns,
          timestamp: nowIso()
        };
        yield { type: "conversation_end", runId, result: replayResult, timestamp: nowIso() };
        return;
      }
      transcript = [...prior.turns];
      spentCreditsCents = transcript.reduce(
        (sum, t) => sum + centsFromUsd(t.usage?.costUsd ?? 0),
        0
      );
      startedAt = prior.startedAt;
      resumed = true;
    } else {
      await options.journal.beginRun(runId, startedAt);
    }
  }
  const startedAtMs = Date.now();
  if (resumed) {
    yield {
      type: "conversation_resumed",
      runId,
      participants: conversation.participants.map((p) => p.name),
      // Snapshot the resumed transcript — the live `transcript` array gets
      // pushed to as the run continues, so handing the bare reference to a
      // subscriber would leak future writes into a past event.
      transcript: [...transcript],
      timestamp: nowIso()
    };
  } else {
    yield {
      type: "conversation_start",
      runId,
      participants: conversation.participants.map((p) => p.name),
      seed: options.seed,
      timestamp: startedAt
    };
  }
  let currentInput = transcript.length === 0 ? options.seed : transcript[transcript.length - 1]?.text ?? options.seed;
  let halt;
  const initialOffset = transcript.length;
  for (let turnIndex = initialOffset; turnIndex < conversation.policy.maxTurns; turnIndex++) {
    if (options.signal?.aborted) {
      halt = { kind: "abort" };
      break;
    }
    if (conversation.policy.maxCreditsCents !== void 0 && spentCreditsCents >= conversation.policy.maxCreditsCents) {
      halt = {
        kind: "max_credits",
        spentCents: spentCreditsCents,
        capCents: conversation.policy.maxCreditsCents
      };
      break;
    }
    const speakerIdx = selectSpeaker(
      conversation.policy.turnOrder,
      conversation.participants.length,
      { transcript, turnIndex, spentCreditsCents }
    );
    const speaker = conversation.participants[speakerIdx];
    if (!speaker) {
      throw new BackendTransportError(
        "conversation",
        `turnOrder selector returned out-of-range index ${speakerIdx} for ${conversation.participants.length} participants`
      );
    }
    const tid = turnId(runId, turnIndex, speaker.name);
    const callPolicy = speaker.callPolicy ?? conversation.policy.defaultCallPolicy;
    const breaker = breakers.get(speaker.name);
    if (!breaker) {
      throw new BackendTransportError(
        "conversation",
        `internal: no circuit-breaker state registered for participant '${speaker.name}'`
      );
    }
    const isRetryable = callPolicy?.isRetryable ?? defaultIsRetryable;
    const totalAttempts = 1 + (callPolicy?.maxRetries ?? 0);
    yield {
      type: "turn_start",
      runId,
      index: turnIndex,
      speaker: speaker.name,
      turnId: tid,
      attempt: 1,
      timestamp: nowIso()
    };
    let aggregator;
    let attemptCount = 0;
    let lastError;
    let breakerOpenFailure;
    for (let attempt = 1; attempt <= totalAttempts; attempt++) {
      attemptCount = attempt;
      try {
        breaker.preflight(speaker.name);
      } catch (err) {
        breakerOpenFailure = err;
        break;
      }
      if (attempt > 1) {
        yield {
          type: "turn_retry",
          runId,
          index: turnIndex,
          speaker: speaker.name,
          turnId: tid,
          attempt,
          reason: lastError instanceof Error ? lastError.message : String(lastError),
          timestamp: nowIso()
        };
      }
      const perAttempt = makePerAttemptSignal(options.signal, callPolicy?.perAttemptDeadlineMs);
      const localAgg = new TurnAggregator({
        index: turnIndex,
        speaker: speaker.name,
        startedAt: nowIso()
      });
      try {
        for await (const delta of driveSingleAttempt({
          speaker,
          participants: conversation.participants,
          input: currentInput,
          turnIndex,
          runId,
          turnId: tid,
          transcript,
          signal: perAttempt.signal,
          aggregator: localAgg,
          propagatedHeaders: buildForwardHeaders({
            inboundDepth,
            // When the participant elects to pay for its own outbound calls,
            // drop the forwarded user identity so the downstream gateway
            // bills the participant's own credentials instead. The backend
            // brings its own `Authorization` header at construction time
            // (e.g. `createOpenAICompatibleBackend({ apiKey: sk-tan-AGENT })`);
            // omitting the forwarded header is what flips the billing target.
            forwardedAuthorization: resolveAuthForwarding(speaker, {
              transcript,
              turnIndex,
              spentCreditsCents
            }) ? forwardedAuthorization : void 0,
            runId,
            turnId: tid,
            parentTurnId: options.parentTurnId,
            speaker: speaker.name
          })
        })) {
          yield {
            type: "turn_text_delta",
            runId,
            index: turnIndex,
            speaker: speaker.name,
            turnId: tid,
            text: delta.text,
            timestamp: delta.timestamp
          };
        }
        perAttempt.dispose();
        breaker.recordSuccess();
        aggregator = localAgg;
        break;
      } catch (err) {
        perAttempt.dispose();
        breaker.recordFailure();
        lastError = perAttempt.getDeadlineError() ?? err;
        if (attempt >= totalAttempts || !isRetryable(lastError)) {
          break;
        }
        await sleep(computeBackoff(callPolicy?.retryBackoffMs, attempt));
      }
    }
    if (!aggregator) {
      const failure = breakerOpenFailure ?? lastError;
      const message = failure instanceof Error ? failure.message : String(failure);
      halt = { kind: "participant_error", participant: speaker.name, message };
      break;
    }
    const turn = aggregator.toTurn({ turnId: tid, attempts: attemptCount });
    transcript.push(turn);
    spentCreditsCents += centsFromUsd(turn.usage?.costUsd ?? 0);
    if (options.journal) {
      await options.journal.appendTurn(runId, turn);
    }
    yield { type: "turn_end", runId, turn, timestamp: nowIso() };
    if (conversation.policy.haltOn) {
      const haltCtx = {
        transcript,
        lastTurn: turn,
        turnIndex,
        spentCreditsCents
      };
      const decision = await conversation.policy.haltOn(haltCtx);
      if (decision === true) {
        halt = { kind: "predicate", reason: "predicate_true" };
        break;
      }
      if (typeof decision === "object" && decision !== null && decision.halted) {
        halt = { kind: "predicate", reason: decision.reason };
        break;
      }
    }
    currentInput = turn.text;
  }
  if (!halt) halt = { kind: "max_turns", turns: transcript.length };
  const endedAt = nowIso();
  const result = {
    runId,
    transcript,
    turns: transcript.length,
    spentCreditsCents,
    halted: halt,
    durationMs: Date.now() - startedAtMs,
    startedAt,
    endedAt
  };
  if (options.journal) {
    await options.journal.recordHalt(runId, halt, endedAt);
  }
  yield { type: "conversation_end", runId, result, timestamp: endedAt };
}
async function* driveSingleAttempt(args) {
  const task = {
    id: args.turnId,
    intent: args.input,
    metadata: {
      runId: args.runId,
      turnId: args.turnId,
      turnIndex: args.turnIndex,
      speaker: args.speaker.name,
      participants: args.participants.map((p) => p.name)
    }
  };
  const knowledge = passingReadiness(task.id);
  const messages = buildMessagesFor(args.speaker.name, args.transcript, args.input);
  const backendInput = { task, message: args.input, messages };
  const startCtx = {
    task,
    knowledge,
    signal: args.signal,
    runId: args.runId,
    turnId: args.turnId,
    propagatedHeaders: args.propagatedHeaders
  };
  const session = args.speaker.backend.start ? touchSession(await args.speaker.backend.start(backendInput, startCtx)) : newRuntimeSession(args.speaker.backend.kind, void 0, {
    runId: args.runId,
    turnIndex: args.turnIndex,
    turnId: args.turnId,
    speaker: args.speaker.name
  });
  const streamCtx = {
    task,
    knowledge,
    session,
    signal: args.signal,
    runId: args.runId,
    turnId: args.turnId,
    propagatedHeaders: args.propagatedHeaders
  };
  for await (const event of args.speaker.backend.stream(backendInput, streamCtx)) {
    if (args.signal.aborted) {
      const reason = args.signal.reason;
      throw reason instanceof Error ? reason : new Error("aborted");
    }
    if (event.type === "text_delta") {
      args.aggregator.appendText(event.text);
      yield { text: event.text, timestamp: event.timestamp };
    } else if (event.type === "llm_call") {
      args.aggregator.recordUsage(event);
    } else if (event.type === "final") {
      args.aggregator.adoptFinalText(event.text);
    }
  }
}
var TurnAggregator = class {
  constructor(base) {
    this.base = base;
  }
  base;
  text = "";
  adoptedFinal = false;
  usage;
  appendText(text) {
    if (this.adoptedFinal) return;
    this.text += text;
  }
  /**
   * Use the backend's `final.text` only when no streamed deltas were observed.
   * Some backends emit deltas AND a final summary; treating both as content
   * would double-count.
   */
  adoptFinalText(text) {
    if (!text) return;
    if (this.text.length > 0) return;
    this.text = text;
    this.adoptedFinal = true;
  }
  recordUsage(event) {
    const u = this.usage ?? {};
    if (event.tokensIn !== void 0) u.tokensIn = (u.tokensIn ?? 0) + event.tokensIn;
    if (event.tokensOut !== void 0) u.tokensOut = (u.tokensOut ?? 0) + event.tokensOut;
    if (event.costUsd !== void 0) u.costUsd = (u.costUsd ?? 0) + event.costUsd;
    if (event.latencyMs !== void 0) u.latencyMs = event.latencyMs;
    if (event.model !== void 0) u.model = event.model;
    this.usage = u;
  }
  toTurn(meta) {
    return {
      index: this.base.index,
      speaker: this.base.speaker,
      turnId: meta.turnId,
      text: this.text.trim(),
      usage: this.usage,
      attempts: meta.attempts,
      startedAt: this.base.startedAt,
      endedAt: nowIso()
    };
  }
};
function buildMessagesFor(speakerName, transcript, currentInput) {
  const messages = [];
  for (const turn of transcript) {
    if (turn.speaker === speakerName) {
      messages.push({ role: "assistant", content: turn.text });
    } else {
      messages.push({ role: "user", content: `[${turn.speaker}] ${turn.text}` });
    }
  }
  if (currentInput) messages.push({ role: "user", content: currentInput });
  return messages;
}
function resolveAuthForwarding(participant, state) {
  const decision = typeof participant.authSource === "function" ? participant.authSource(state) : participant.authSource ?? "forward-user";
  return decision === "forward-user";
}
function selectSpeaker(order, participantCount, state) {
  const resolved = order ?? (participantCount === 2 ? "alternate" : "round-robin");
  if (resolved === "alternate" || resolved === "round-robin") {
    return state.turnIndex % participantCount;
  }
  if (typeof resolved === "function") {
    const idx = resolved(state);
    if (!Number.isInteger(idx) || idx < 0 || idx >= participantCount) {
      throw new BackendTransportError(
        "conversation",
        `turnOrder function returned invalid index ${String(idx)} for ${participantCount} participants`
      );
    }
    return idx;
  }
  throw new BackendTransportError("conversation", `unknown turnOrder: ${String(resolved)}`);
}
function centsFromUsd(usd) {
  return Math.round(usd * 100);
}
function passingReadiness(taskId) {
  return {
    taskId,
    readinessScore: 1,
    blockingMissingRequirements: [],
    nonBlockingGaps: [],
    recommendedAction: "run_agent",
    bundle: {
      taskId,
      requirements: [],
      evidenceIds: [],
      claimIds: [],
      wikiPageIds: [],
      userAnswers: {},
      missing: [],
      readinessScore: 1
    },
    severity: "info",
    reason: "conversation-mode: readiness gating not applied per-turn"
  };
}

// src/conversation/conversation-backend.ts
function createConversationBackend(options) {
  const kind = options.kind ?? "conversation";
  return {
    kind,
    start(_input, context) {
      return newRuntimeSession(kind, context.requestedSessionId, {
        participants: options.conversation.participants.map((p) => p.name)
      });
    },
    async *stream(input, context) {
      const seed = input.message ?? input.messages?.at(-1)?.content ?? context.task.intent;
      const task = context.task;
      const session = context.session;
      yield { type: "backend_start", task, session, backend: kind, timestamp: nowIso() };
      let finalText = "";
      let totalCostUsd = 0;
      let totalTokensIn = 0;
      let totalTokensOut = 0;
      const inboundDepth = parseInboundDepth(context.propagatedHeaders);
      for await (const event of runConversationStream(options.conversation, {
        seed,
        signal: context.signal,
        runId: context.runId,
        propagatedHeaders: context.propagatedHeaders,
        inboundDepth,
        parentTurnId: context.turnId
      })) {
        if (event.type === "turn_end") {
          const tagged = `[${event.turn.speaker}] ${event.turn.text}
`;
          finalText += tagged;
          yield { type: "text_delta", task, session, text: tagged, timestamp: event.timestamp };
          if (event.turn.usage) {
            const u = event.turn.usage;
            if (u.costUsd !== void 0) totalCostUsd += u.costUsd;
            if (u.tokensIn !== void 0) totalTokensIn += u.tokensIn;
            if (u.tokensOut !== void 0) totalTokensOut += u.tokensOut;
            yield {
              type: "llm_call",
              task,
              session,
              model: u.model ?? `${kind}/${event.turn.speaker}`,
              tokensIn: u.tokensIn,
              tokensOut: u.tokensOut,
              costUsd: u.costUsd,
              latencyMs: u.latencyMs,
              timestamp: event.timestamp
            };
          }
        } else if (event.type === "conversation_end") {
          const halt = event.result.halted;
          yield {
            type: "final",
            task,
            session,
            status: halt.kind === "participant_error" ? "failed" : "completed",
            reason: describeHalt(halt),
            text: finalText.trim(),
            metadata: {
              conversationRunId: event.result.runId,
              turns: event.result.turns,
              spentCreditsCents: event.result.spentCreditsCents,
              halted: halt,
              durationMs: event.result.durationMs,
              tokensIn: totalTokensIn,
              tokensOut: totalTokensOut,
              costUsd: totalCostUsd
            },
            timestamp: event.timestamp
          };
        }
      }
      yield { type: "backend_end", task, session, backend: kind, timestamp: nowIso() };
    }
  };
}
function parseInboundDepth(headers) {
  if (!headers) return 0;
  const raw = headers[FORWARD_HEADERS.depth];
  if (raw === void 0) return 0;
  try {
    return readDepth({ [FORWARD_HEADERS.depth]: raw });
  } catch {
    return 0;
  }
}
function describeHalt(halt) {
  switch (halt.kind) {
    case "max_turns":
      return `max_turns (${halt.turns})`;
    case "max_credits":
      return `max_credits (${halt.spentCents}/${halt.capCents}\xA2)`;
    case "predicate":
      return `predicate: ${halt.reason}`;
    case "abort":
      return "abort";
    case "participant_error":
      return `participant_error[${halt.participant}]: ${halt.message}`;
  }
}

// src/conversation/define-conversation.ts
function defineConversation(input) {
  if (input.participants.length < 2) {
    throw new ValidationError(
      `Conversation requires at least 2 participants; received ${input.participants.length}.`
    );
  }
  const seen = /* @__PURE__ */ new Set();
  for (const p of input.participants) {
    if (!p.name || p.name.trim() === "") {
      throw new ValidationError("Conversation participant.name must be a non-empty string.");
    }
    if (seen.has(p.name)) {
      throw new ValidationError(
        `Conversation participant names must be unique within a Conversation; '${p.name}' appears more than once.`
      );
    }
    seen.add(p.name);
    if (!p.backend || typeof p.backend.stream !== "function") {
      throw new ValidationError(
        `Conversation participant '${p.name}' is missing a backend with a stream() method.`
      );
    }
  }
  const policy = normalizePolicy(input.policy, input.participants.length);
  return {
    participants: input.participants,
    policy
  };
}
function normalizePolicy(policy, participantCount) {
  if (!Number.isInteger(policy.maxTurns) || policy.maxTurns < 1) {
    throw new ValidationError(
      `ConversationPolicy.maxTurns must be a positive integer; received ${String(policy.maxTurns)}.`
    );
  }
  if (policy.maxCreditsCents !== void 0 && (!Number.isFinite(policy.maxCreditsCents) || policy.maxCreditsCents < 0)) {
    throw new ValidationError(
      `ConversationPolicy.maxCreditsCents must be a non-negative finite number when set; received ${String(
        policy.maxCreditsCents
      )}.`
    );
  }
  const turnOrder = policy.turnOrder ?? (participantCount === 2 ? "alternate" : "round-robin");
  if (turnOrder === "alternate" && participantCount !== 2) {
    throw new ValidationError(
      `ConversationPolicy.turnOrder 'alternate' requires exactly 2 participants; received ${participantCount}. Use 'round-robin' or a custom selector for N-party conversations.`
    );
  }
  return { ...policy, turnOrder };
}

// src/conversation/journal.ts
var InMemoryConversationJournal = class {
  entries = /* @__PURE__ */ new Map();
  async loadRun(runId) {
    const entry = this.entries.get(runId);
    if (!entry) return void 0;
    return {
      runId: entry.runId,
      startedAt: entry.startedAt,
      halted: entry.halted,
      endedAt: entry.endedAt,
      turns: [...entry.turns]
    };
  }
  async beginRun(runId, startedAt) {
    const existing = this.entries.get(runId);
    if (existing) {
      if (existing.startedAt !== startedAt) {
        throw new Error(
          `runId '${runId}' already exists with startedAt=${existing.startedAt}; refusing to overwrite with ${startedAt}`
        );
      }
      return;
    }
    this.entries.set(runId, { runId, startedAt, turns: [] });
  }
  async appendTurn(runId, turn) {
    const entry = this.entries.get(runId);
    if (!entry) {
      throw new Error(
        `appendTurn called for unknown runId '${runId}'; call beginRun first or use the runner which handles it`
      );
    }
    if (entry.halted) {
      throw new Error(
        `cannot append turn to halted run '${runId}' (halt reason: ${JSON.stringify(entry.halted)})`
      );
    }
    entry.turns.push(turn);
  }
  async recordHalt(runId, halt, endedAt) {
    const entry = this.entries.get(runId);
    if (!entry) {
      throw new Error(`recordHalt called for unknown runId '${runId}'`);
    }
    entry.halted = halt;
    entry.endedAt = endedAt;
  }
};
var FileConversationJournal = class {
  constructor(path) {
    this.path = path;
  }
  path;
  async loadRun(runId) {
    const fs = await import("fs/promises");
    let text;
    try {
      text = await fs.readFile(this.path, "utf8");
    } catch (err) {
      if (isNoEntError(err)) return void 0;
      throw err;
    }
    const lines = text.split("\n").filter((line) => line.length > 0);
    let entry;
    for (const line of lines) {
      const record = JSON.parse(line);
      if (record.runId !== runId) continue;
      if (record.kind === "begin") {
        entry = { runId, startedAt: record.startedAt, turns: [] };
      } else if (record.kind === "turn") {
        if (!entry) {
          throw new Error(
            `journal corrupted: turn record for runId '${runId}' precedes its begin record`
          );
        }
        entry.turns.push(record.turn);
      } else if (record.kind === "halt") {
        if (!entry) {
          throw new Error(
            `journal corrupted: halt record for runId '${runId}' precedes its begin record`
          );
        }
        entry.halted = record.halted;
        entry.endedAt = record.endedAt;
      }
    }
    return entry;
  }
  async beginRun(runId, startedAt) {
    const existing = await this.loadRun(runId);
    if (existing) {
      if (existing.startedAt !== startedAt) {
        throw new Error(
          `runId '${runId}' already exists in ${this.path} with startedAt=${existing.startedAt}; refusing to overwrite with ${startedAt}`
        );
      }
      return;
    }
    await this.appendRecord({ kind: "begin", runId, startedAt });
  }
  async appendTurn(runId, turn) {
    await this.appendRecord({ kind: "turn", runId, turn });
  }
  async recordHalt(runId, halt, endedAt) {
    await this.appendRecord({ kind: "halt", runId, halted: halt, endedAt });
  }
  async appendRecord(record) {
    const fs = await import("fs/promises");
    const path = await import("path");
    await fs.mkdir(path.dirname(this.path), { recursive: true });
    const fh = await fs.open(this.path, "a");
    try {
      await fh.write(`${JSON.stringify(record)}
`);
      await fh.sync();
    } finally {
      await fh.close();
    }
  }
};
function isNoEntError(err) {
  return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
}

// src/conversation/journal-sql.ts
function d1ToSqlAdapter(db) {
  return {
    async exec(sql, params = []) {
      const stmt = db.prepare(sql);
      const bound = params.length > 0 ? stmt.bind(...params) : stmt;
      const result = await bound.run();
      const meta = result.meta;
      return { rowsAffected: meta?.rows_written ?? meta?.changes ?? 0 };
    },
    async query(sql, params = []) {
      const stmt = db.prepare(sql);
      const bound = params.length > 0 ? stmt.bind(...params) : stmt;
      const result = await bound.all();
      return result.results ?? [];
    }
  };
}
var RUNS_TABLE_DDL = (table) => `
  CREATE TABLE IF NOT EXISTS ${table}_runs (
    run_id TEXT PRIMARY KEY,
    started_at TEXT NOT NULL,
    halted_kind TEXT,
    halted_payload TEXT,
    ended_at TEXT
  )
`;
var TURNS_TABLE_DDL = (table) => `
  CREATE TABLE IF NOT EXISTS ${table}_turns (
    run_id TEXT NOT NULL,
    turn_index INTEGER NOT NULL,
    payload TEXT NOT NULL,
    PRIMARY KEY (run_id, turn_index)
  )
`;
var TURNS_INDEX_DDL = (table) => `
  CREATE INDEX IF NOT EXISTS idx_${table}_turns_run ON ${table}_turns (run_id, turn_index)
`;
var SqlConversationJournal = class {
  /**
   * @param db    SQL adapter (D1, postgres, sqlite, libSQL — all work)
   * @param table Table-name prefix; the journal creates `${table}_runs` and
   *              `${table}_turns`. Lets multiple journals share a database
   *              without colliding (e.g. one per product surface).
   */
  constructor(db, table = "agent_runtime_journal") {
    this.db = db;
    this.table = table;
  }
  db;
  table;
  /**
   * Create the journal's tables if absent. Idempotent. Call once at deploy
   * (or at app boot) — running on every request is harmless but adds latency.
   */
  async migrate() {
    await this.db.exec(RUNS_TABLE_DDL(this.table));
    await this.db.exec(TURNS_TABLE_DDL(this.table));
    await this.db.exec(TURNS_INDEX_DDL(this.table));
  }
  async loadRun(runId) {
    const runs = await this.db.query(
      `SELECT run_id, started_at, halted_kind, halted_payload, ended_at FROM ${this.table}_runs WHERE run_id = ?`,
      [runId]
    );
    const row = runs[0];
    if (!row) return void 0;
    const turns = await this.db.query(
      `SELECT payload, turn_index FROM ${this.table}_turns WHERE run_id = ? ORDER BY turn_index ASC`,
      [runId]
    );
    return {
      runId: row.run_id,
      startedAt: row.started_at,
      halted: row.halted_payload ? JSON.parse(row.halted_payload) : void 0,
      endedAt: row.ended_at ?? void 0,
      turns: turns.map((t) => JSON.parse(t.payload))
    };
  }
  async beginRun(runId, startedAt) {
    const existing = await this.db.query(
      `SELECT started_at FROM ${this.table}_runs WHERE run_id = ?`,
      [runId]
    );
    if (existing.length > 0) {
      if (existing[0]?.started_at !== startedAt) {
        throw new Error(
          `runId '${runId}' already exists with startedAt=${existing[0]?.started_at}; refusing to overwrite with ${startedAt}`
        );
      }
      return;
    }
    await this.db.exec(`INSERT INTO ${this.table}_runs (run_id, started_at) VALUES (?, ?)`, [
      runId,
      startedAt
    ]);
  }
  async appendTurn(runId, turn) {
    const halted = await this.db.query(
      `SELECT halted_kind FROM ${this.table}_runs WHERE run_id = ?`,
      [runId]
    );
    if (halted.length === 0) {
      throw new Error(
        `appendTurn called for unknown runId '${runId}'; call beginRun first or use the runner which handles it`
      );
    }
    if (halted[0]?.halted_kind) {
      throw new Error(
        `cannot append turn to halted run '${runId}' (halt kind: ${halted[0]?.halted_kind})`
      );
    }
    await this.db.exec(
      `INSERT INTO ${this.table}_turns (run_id, turn_index, payload) VALUES (?, ?, ?)`,
      [runId, turn.index, JSON.stringify(turn)]
    );
  }
  async recordHalt(runId, halt, endedAt) {
    const rs = await this.db.exec(
      `UPDATE ${this.table}_runs SET halted_kind = ?, halted_payload = ?, ended_at = ? WHERE run_id = ?`,
      [halt.kind, JSON.stringify(halt), endedAt, runId]
    );
    if (rs.rowsAffected === 0) {
      throw new Error(`recordHalt called for unknown runId '${runId}'`);
    }
  }
};

// src/conversation/run-persona.ts
function withProfilePrompt(inner, systemPrompt, counter) {
  return {
    kind: inner.kind,
    start: inner.start ? (input, ctx) => inner.start(input, ctx) : void 0,
    resume: inner.resume ? (session, input, ctx) => inner.resume(session, input, ctx) : void 0,
    stop: inner.stop ? (session, reason) => inner.stop(session, reason) : void 0,
    async *stream(input, context) {
      const base = input.messages ?? (input.message ? [{ role: "user", content: input.message }] : []);
      const messages = base[0]?.role === "system" ? base : [{ role: "system", content: systemPrompt }, ...base];
      for await (const event of inner.stream({ ...input, messages }, context)) {
        if (counter && event.type === "llm_call") {
          counter.tokensIn += event.tokensIn ?? 0;
          counter.tokensOut += event.tokensOut ?? 0;
          counter.costUsd += event.costUsd ?? 0;
        }
        yield event;
      }
    }
  };
}
function scriptedPersonaBackend(turns) {
  let idx = 0;
  return createIterableBackend({
    kind: "persona-user",
    async *stream(_input, context) {
      const text = turns[idx];
      if (text === void 0) {
        throw new Error(
          `persona-user: ran out of scripted turns at index ${idx} (had ${turns.length})`
        );
      }
      idx += 1;
      yield {
        type: "text_delta",
        task: context.task,
        session: context.session,
        text,
        timestamp: (/* @__PURE__ */ new Date()).toISOString()
      };
    }
  });
}
async function runPersonaConversation(opts) {
  const counter = { tokensIn: 0, tokensOut: 0, costUsd: 0 };
  const workerName = opts.workerName ?? "agent";
  const worker = withProfilePrompt(
    opts.backendFor(opts.worker, "worker"),
    opts.systemPromptOf(opts.worker),
    counter
  );
  let persona;
  let maxTurns;
  if (opts.persona.kind === "scripted") {
    if (opts.persona.turns.length === 0) {
      throw new Error("runPersonaConversation: scripted persona has no turns");
    }
    persona = scriptedPersonaBackend(opts.persona.turns);
    maxTurns = opts.maxTurns ?? 2 * opts.persona.turns.length;
  } else {
    persona = withProfilePrompt(
      opts.backendFor(opts.persona.profile, "persona"),
      opts.systemPromptOf(opts.persona.profile)
    );
    if (opts.maxTurns === void 0) {
      throw new Error("runPersonaConversation: maxTurns is required for a profile-driven persona");
    }
    maxTurns = opts.maxTurns;
  }
  const conversation = defineConversation({
    // Persona leads (participant 0): the seed routes to it, it produces the
    // user turn, the worker answers, alternate.
    participants: [
      { name: "user", backend: persona },
      { name: workerName, backend: worker }
    ],
    policy: { maxTurns, turnOrder: "alternate", ...opts.haltOn ? { haltOn: opts.haltOn } : {} }
  });
  const result = await runConversation(conversation, {
    seed: opts.seed ?? "Begin.",
    signal: opts.signal
  });
  const costUsd = counter.costUsd > 0 ? counter.costUsd : opts.persona.kind === "scripted" ? result.spentCreditsCents / 100 : 0;
  return {
    transcript: result.transcript,
    turns: result.turns,
    halted: result.halted,
    costUsd,
    tokensIn: counter.tokensIn,
    tokensOut: counter.tokensOut
  };
}
function runPersonaDispatch(config) {
  return async (worker, scenario, ctx) => {
    const model = worker.model?.default ?? "unknown";
    const maximumCharge = typeof config.maximumCharge === "function" ? config.maximumCharge(worker, scenario) : config.maximumCharge;
    const paid = await ctx.cost.runPaidCall({
      channel: "agent",
      actor: "persona-conversation",
      model,
      signal: ctx.signal,
      ...maximumCharge ? { maximumCharge } : {},
      execute: (executionSignal) => runPersonaConversation({
        worker,
        persona: config.personaOf(scenario),
        backendFor: config.backendFor,
        systemPromptOf: config.systemPromptOf,
        maxTurns: config.maxTurns?.(scenario),
        seed: config.seed?.(scenario),
        signal: executionSignal,
        workerName: config.workerName
      }),
      receipt: (result2) => ({
        model,
        inputTokens: result2.tokensIn,
        outputTokens: result2.tokensOut,
        ...result2.costUsd > 0 ? { actualCostUsd: result2.costUsd } : {}
      })
    });
    if (!paid.succeeded) throw paid.error;
    const result = paid.value;
    return config.artifactOf(result.transcript, scenario);
  };
}

// src/durable/chat-engine.ts
var encoder = new TextEncoder();
function encodeLine(event) {
  return encoder.encode(`${JSON.stringify(event)}
`);
}
function defaultLog(message, meta) {
  if (meta) console.error(message, meta);
  else console.error(message);
}
function handleChatTurn(input) {
  const log = input.log ?? defaultLog;
  const { identity, hooks } = input;
  const body = new ReadableStream({
    start: async (controller) => {
      const emit2 = async (event) => {
        controller.enqueue(encodeLine(event));
        if (hooks.onEvent) {
          try {
            await hooks.onEvent(event);
          } catch (err) {
            log("[chat-engine] onEvent hook threw", {
              error: err instanceof Error ? err.message : String(err)
            });
          }
        }
      };
      try {
        await emit2({
          type: "session.run.started",
          data: {
            sessionId: identity.sessionId,
            tenantId: identity.tenantId,
            turnIndex: identity.turnIndex
          }
        });
        const producer = hooks.produce();
        for await (const event of producer.stream) {
          await emit2(event);
        }
        const rawFinal = producer.finalText();
        const finalText = hooks.transformFinalText ? await hooks.transformFinalText(rawFinal) : rawFinal;
        await hooks.persistAssistantMessage({ identity, finalText });
        if (hooks.onTurnComplete) {
          try {
            await hooks.onTurnComplete({ identity, finalText });
          } catch (err) {
            log("[chat-engine] onTurnComplete threw", {
              error: err instanceof Error ? err.message : String(err)
            });
          }
        }
        await emit2({
          type: "session.run.completed",
          data: { sessionId: identity.sessionId }
        });
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        log("[chat-engine] turn failed", { error: message });
        await emit2({ type: "error", data: { message } });
        await emit2({
          type: "session.run.failed",
          data: { sessionId: identity.sessionId, message }
        });
      } finally {
        if (hooks.traceFlush) {
          const flush = hooks.traceFlush().catch(
            (err) => log("[chat-engine] traceFlush threw", {
              error: err instanceof Error ? err.message : String(err)
            })
          );
          if (input.waitUntil) input.waitUntil(flush);
          else await flush;
        }
        controller.close();
      }
    }
  });
  return { body, contentType: "application/x-ndjson" };
}

// src/durable/execution-handle.ts
function deriveExecutionId(input) {
  return `${input.projectId}:${input.sessionId}:${input.turnIndex}`;
}

// src/improvement/profile-diff-proposer.ts
import { canonicalJson } from "@tangle-network/agent-eval";
import {
  changedAgentProfileAxes
} from "@tangle-network/agent-interface";
function profileDiffProposer(options) {
  return {
    kind: "agent-profile-diff",
    async propose(ctx) {
      if (typeof ctx.currentSurface !== "string") {
        throw new Error("profileDiffProposer requires a serialized AgentProfile surface");
      }
      const profile = parseSerializedProfile(ctx.currentSurface);
      const proposals = await options.proposeDiffs({
        ...ctx,
        profile
      });
      const candidates = [];
      const normalizedBaseline = applyExactAgentProfileDiff(
        profile,
        { schemaVersion: 1, kind: "agent-profile-diff" },
        "profile diff baseline"
      );
      const seen = /* @__PURE__ */ new Set([canonicalJson(normalizedBaseline)]);
      for (const [index, proposal] of proposals.entries()) {
        if (candidates.length >= ctx.populationSize || ctx.signal.aborted) break;
        const diff = parseExactAgentProfileDiff(proposal.diff, `profile diff proposal ${index}`);
        const axes = changedAgentProfileAxes(diff);
        if (axes.length === 0) continue;
        const candidate = applyExactAgentProfileDiff(
          profile,
          diff,
          `profile diff candidate ${index}`
        );
        const surface = canonicalJson(candidate);
        if (seen.has(surface)) continue;
        seen.add(surface);
        candidates.push({
          surface,
          label: proposal.label ?? diff.title ?? diff.id ?? `Change ${axes.join(", ")}`,
          rationale: candidateRationale(proposal, diff, axes)
        });
      }
      return candidates;
    }
  };
}
function candidateRationale(proposal, diff, axes) {
  const reason = proposal.rationale ?? diff.rationale ?? `Changes profile axes: ${axes.join(", ")}.`;
  const source = diff.source ? [diff.source.kind, ...diff.source.artifacts ?? []].join(" \xB7 ") : "source unspecified";
  return `${reason} Source: ${source}. Diff: ${canonicalCandidateDigest(diff)}.`;
}
function parseSerializedProfile(surface) {
  let raw;
  try {
    raw = JSON.parse(surface);
  } catch (cause) {
    throw new Error("profileDiffProposer current surface is not valid AgentProfile JSON", {
      cause
    });
  }
  return parseExactAgentProfile(raw, "profileDiffProposer current profile");
}

// src/improvement/reflective-generator.ts
import { spawnSync } from "child_process";
function reflectiveGenerator(opts) {
  return {
    kind: "reflective",
    async generate({ worktreePath, findings }) {
      const batch = await opts.improvementAdapter.proposeFromFindings(findings);
      if (batch.edits.length === 0) return { applied: false, summary: "" };
      let applied = 0;
      for (const edit of batch.edits) {
        if (applyPatch(edit.patch, worktreePath)) applied++;
      }
      if (applied === 0) return { applied: false, summary: "" };
      const summary = batch.edits.length === 1 ? batch.edits[0].summary : `analyst: ${applied} surface edit${applied === 1 ? "" : "s"}`;
      return { applied: true, summary };
    }
  };
}
function applyPatch(patch, cwd) {
  const result = spawnSync("git", ["apply", "--whitespace=fix", "-p0", "-"], {
    cwd,
    input: patch,
    encoding: "utf-8"
  });
  return result.status === 0;
}

// src/readiness.ts
var DEFAULT_MINIMUM_READINESS_SCORE = 0.7;
function decideKnowledgeReadiness(report, options = {}) {
  const minimumScore = options.minimumScore ?? DEFAULT_MINIMUM_READINESS_SCORE;
  if (!Number.isFinite(minimumScore) || minimumScore < 0 || minimumScore > 1) {
    throw new ValidationError(
      `minimumScore must be a finite number in [0, 1]; received ${String(minimumScore)}`
    );
  }
  const blockingGapIds = report.blockingMissingRequirements.map((requirement) => requirement.id);
  const nonBlockingGapIds = report.nonBlockingGaps.map((requirement) => requirement.id);
  if (blockingGapIds.length > 0) {
    return {
      passed: false,
      status: "blocked",
      reason: report.reason,
      readinessScore: report.readinessScore,
      recommendedAction: report.recommendedAction,
      severity: report.severity,
      blockingGapIds,
      nonBlockingGapIds
    };
  }
  if (report.readinessScore < minimumScore) {
    return {
      passed: false,
      status: "caveat",
      reason: `Knowledge readiness score ${report.readinessScore.toFixed(3)} is below minimum ${minimumScore.toFixed(3)}.`,
      readinessScore: report.readinessScore,
      recommendedAction: report.recommendedAction,
      severity: report.severity,
      blockingGapIds,
      nonBlockingGapIds
    };
  }
  return {
    passed: true,
    status: "ready",
    reason: report.reason,
    readinessScore: report.readinessScore,
    recommendedAction: report.recommendedAction,
    severity: report.severity,
    blockingGapIds,
    nonBlockingGapIds
  };
}

// src/resolve-agent-backend.ts
function resolveAgentBackend(opts) {
  switch (opts.kind) {
    case "router":
    case "tcloud":
    case "cli-bridge": {
      const passthrough = {};
      if (opts.tools !== void 0) passthrough.tools = opts.tools;
      if (opts.toolChoice !== void 0) passthrough.toolChoice = opts.toolChoice;
      if (opts.responseFormat !== void 0) passthrough.responseFormat = opts.responseFormat;
      if (opts.temperature !== void 0) passthrough.temperature = opts.temperature;
      if (opts.maxTokens !== void 0) passthrough.maxTokens = opts.maxTokens;
      if (opts.fetchImpl !== void 0) passthrough.fetchImpl = opts.fetchImpl;
      if (opts.retry !== void 0) passthrough.retry = opts.retry;
      return createOpenAICompatibleBackend({
        apiKey: opts.apiKey,
        baseUrl: opts.baseUrl,
        model: opts.model,
        kind: opts.label ?? opts.kind,
        ...passthrough
      });
    }
    case "sandbox": {
      if (!opts.sandboxBackend) {
        throw new Error("resolveAgentBackend: kind 'sandbox' requires opts.sandboxBackend");
      }
      return opts.sandboxBackend();
    }
  }
}

// src/run.ts
import {
  acquisitionPlansForKnowledgeGaps,
  blockingKnowledgeEval,
  FAILURE_CLASSES,
  runAgentControlLoop,
  scoreKnowledgeReadiness,
  userQuestionsForKnowledgeGaps
} from "@tangle-network/agent-eval";
var FAILURE_CLASS_SET = new Set(FAILURE_CLASSES);
function asFailureClass(value) {
  return value && FAILURE_CLASS_SET.has(value) ? value : void 0;
}
function applyRunRecordDefaults(records, scenarioId, controlFailureClass) {
  const fc = asFailureClass(controlFailureClass);
  return records.map((record) => {
    let r = record;
    if (r.scenarioId === void 0) r = { ...r, scenarioId };
    if (r.failureClass === void 0 && fc) r = { ...r, failureClass: fc };
    return r;
  });
}
async function runAgentTask(options) {
  const task = options.task;
  await emit(options.onEvent, { type: "task_start", task });
  await emit(options.onEvent, { type: "readiness_start", task });
  let knowledge = await buildReadiness(task, options.knowledge);
  await emit(options.onEvent, { type: "readiness_end", task, knowledge });
  const questions = userQuestionsForKnowledgeGaps(knowledge.blockingMissingRequirements);
  const acquisitionPlans = acquisitionPlansForKnowledgeGaps([
    ...knowledge.blockingMissingRequirements,
    ...knowledge.nonBlockingGaps
  ]);
  const preflight = await runKnowledgePreflight(
    task,
    questions,
    acquisitionPlans,
    options.knowledge,
    options.onEvent
  );
  if (options.knowledge?.refreshReadiness && (Object.keys(preflight.userAnswers).length > 0 || preflight.acquiredEvidenceIds.length > 0)) {
    await emit(options.onEvent, { type: "readiness_start", task });
    knowledge = await options.knowledge.refreshReadiness({
      task,
      previous: knowledge,
      userAnswers: preflight.userAnswers,
      acquiredEvidenceIds: preflight.acquiredEvidenceIds
    });
    await emit(options.onEvent, { type: "readiness_end", task, knowledge });
  }
  await emit(options.onEvent, { type: "control_start", task, knowledge });
  const scenarioId = options.scenarioId ?? task.id;
  const control = await runAgentControlLoop({
    intent: task.intent,
    budget: task.budget,
    signal: options.signal,
    store: options.store,
    scenarioId,
    projectId: options.projectId,
    variantId: options.variantId,
    observe: ({ history, abortSignal }) => options.adapter.observe({ task, knowledge, history, abortSignal }),
    validate: async ({ state, history, abortSignal }) => {
      const readinessEval = blockingKnowledgeEval(knowledge, {
        minimumScore: options.minimumReadinessScore
      });
      const evals = await options.adapter.validate({
        task,
        knowledge,
        state,
        history,
        abortSignal
      });
      return [readinessEval, ...evals];
    },
    decide: (ctx) => {
      if (isKnowledgeBlocked(ctx.evals)) {
        return options.adapter.onKnowledgeBlocked?.({
          task,
          knowledge,
          questions,
          acquisitionPlans
        }) ?? {
          type: "stop",
          pass: false,
          score: knowledge.readinessScore,
          reason: `knowledge readiness blocked: ${knowledge.reason}`
        };
      }
      return options.adapter.decide(toAgentContext(task, knowledge, ctx));
    },
    act: (action, ctx) => options.adapter.act(action, toAgentContext(task, knowledge, ctx)),
    shouldStop: options.adapter.shouldStop ? (ctx) => options.adapter.shouldStop(toAgentContext(task, knowledge, ctx)) : void 0,
    getActionCostUsd: options.adapter.getActionCostUsd ? ({ action, result, state, evals, history }) => options.adapter.getActionCostUsd({ action, result, task, state, evals, history }) : void 0,
    onStep: (step) => emit(options.onEvent, { type: "control_step", task, step })
  });
  await emit(options.onEvent, { type: "control_end", task, control });
  const status = statusFromControl(control);
  await emit(options.onEvent, { type: "task_end", task, status, reason: control.reason });
  return {
    task,
    status,
    knowledge,
    questions,
    acquisitionPlans,
    userAnswers: preflight.userAnswers,
    acquiredEvidenceIds: preflight.acquiredEvidenceIds,
    control,
    runRecords: applyRunRecordDefaults(
      options.adapter.projectRunRecords?.(control, task) ?? [],
      scenarioId,
      control.failureClass
    )
  };
}
async function* runAgentTaskStream(options) {
  const task = options.task;
  const input = { task, ...options.input ?? {} };
  yield streamEvent({ type: "task_start", task });
  yield streamEvent({ type: "readiness_start", task });
  let knowledge = await buildReadiness(task, options.knowledge);
  const questions = userQuestionsForKnowledgeGaps(knowledge.blockingMissingRequirements);
  const acquisitionPlans = acquisitionPlansForKnowledgeGaps([
    ...knowledge.blockingMissingRequirements,
    ...knowledge.nonBlockingGaps
  ]);
  const preflight = await runKnowledgePreflightStream(
    task,
    questions,
    acquisitionPlans,
    options.knowledge
  );
  for (const event of preflight.events) yield event;
  if (options.knowledge?.refreshReadiness && (Object.keys(preflight.userAnswers).length > 0 || preflight.acquiredEvidenceIds.length > 0)) {
    yield streamEvent({ type: "readiness_start", task });
    knowledge = await options.knowledge.refreshReadiness({
      task,
      previous: knowledge,
      userAnswers: preflight.userAnswers,
      acquiredEvidenceIds: preflight.acquiredEvidenceIds
    });
  }
  const decision = decideKnowledgeReadiness(knowledge, {
    minimumScore: options.minimumReadinessScore
  });
  yield streamEvent({ type: "readiness_end", task, knowledge, decision });
  if (!decision.passed && decision.status === "blocked") {
    const reason = `knowledge readiness blocked: ${decision.reason}`;
    yield streamEvent({ type: "task_end", task, status: "blocked", reason });
    yield streamEvent({ type: "final", task, status: "blocked", reason });
    return;
  }
  const store = options.sessionStore;
  const existing = options.sessionId ? await store?.get(options.sessionId) : void 0;
  const shouldResume = Boolean(options.resume && existing);
  let session = shouldResume && existing ? await resumeBackendSession(options.backend, existing, input, {
    task,
    knowledge,
    signal: options.signal
  }) : await startBackendSession(
    options.backend,
    input,
    { task, knowledge, signal: options.signal },
    options.sessionId
  );
  await store?.put(session);
  const sessionEvent = streamEvent({
    type: shouldResume ? "session_resumed" : "session_created",
    task,
    session
  });
  await store?.appendEvent?.(session.id, sessionEvent);
  yield sessionEvent;
  const backendStart = streamEvent({
    type: "backend_start",
    task,
    session,
    backend: options.backend.kind
  });
  await store?.appendEvent?.(session.id, backendStart);
  yield backendStart;
  let finalText = "";
  try {
    for await (const rawEvent of options.backend.stream(input, {
      task,
      knowledge,
      session,
      signal: options.signal
    })) {
      const event = normalizeBackendStreamEvent(rawEvent, task, session);
      if (event.type === "text_delta") finalText += event.text;
      await store?.appendEvent?.(session.id, event);
      yield event;
    }
    const completedStatus = "completed";
    session = touchSession({ ...session, status: completedStatus });
    await store?.put(session);
    const backendEnd = streamEvent({
      type: "backend_end",
      task,
      session,
      backend: options.backend.kind
    });
    await store?.appendEvent?.(session.id, backendEnd);
    yield backendEnd;
    const reason = "backend completed";
    const taskEnd = streamEvent({ type: "task_end", task, status: completedStatus, reason });
    await store?.appendEvent?.(session.id, taskEnd);
    yield taskEnd;
    const final = streamEvent({
      type: "final",
      task,
      session,
      status: completedStatus,
      reason,
      text: finalText || void 0
    });
    await store?.appendEvent?.(session.id, final);
    yield final;
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    session = touchSession({ ...session, status: options.signal?.aborted ? "aborted" : "failed" });
    await store?.put(session);
    let stopErrorMessage;
    try {
      await options.backend.stop?.(session, message);
    } catch (stopErr) {
      stopErrorMessage = stopErr instanceof Error ? stopErr.message : String(stopErr);
    }
    const combinedMessage = stopErrorMessage ? `${message}; backend stop failed: ${stopErrorMessage}` : message;
    const errorDetail = err instanceof BackendTransportError ? {
      kind: "transport",
      message: combinedMessage,
      status: err.status,
      body: err.body
    } : { kind: "backend", message: combinedMessage };
    const backendError = streamEvent({
      type: "backend_error",
      task,
      session,
      backend: options.backend.kind,
      message: combinedMessage,
      recoverable: !options.signal?.aborted,
      error: errorDetail
    });
    await store?.appendEvent?.(session.id, backendError);
    yield backendError;
    const status = options.signal?.aborted ? "aborted" : "failed";
    const taskEnd = streamEvent({ type: "task_end", task, status, reason: message });
    await store?.appendEvent?.(session.id, taskEnd);
    yield taskEnd;
    const final = streamEvent({
      type: "final",
      task,
      session,
      status,
      reason: message,
      text: finalText || void 0,
      error: errorDetail
    });
    await store?.appendEvent?.(session.id, final);
    yield final;
  }
}
async function runKnowledgePreflight(task, questions, acquisitionPlans, provider, onEvent) {
  let userAnswers = {};
  let acquiredEvidenceIds = [];
  if (questions.length > 0 && provider?.answerQuestions) {
    await emit(onEvent, { type: "questions_start", task, questions });
    userAnswers = await provider.answerQuestions(questions, task);
    await emit(onEvent, { type: "questions_end", task, questions, userAnswers });
  }
  if (acquisitionPlans.length > 0 && provider?.executeAcquisitionPlans) {
    await emit(onEvent, { type: "acquisition_start", task, acquisitionPlans });
    acquiredEvidenceIds = await provider.executeAcquisitionPlans(acquisitionPlans, task);
    await emit(onEvent, {
      type: "acquisition_end",
      task,
      acquisitionPlans,
      acquiredEvidenceIds
    });
  }
  return { userAnswers, acquiredEvidenceIds };
}
async function runKnowledgePreflightStream(task, questions, acquisitionPlans, provider) {
  const events = [];
  let userAnswers = {};
  let acquiredEvidenceIds = [];
  if (questions.length > 0 && provider?.answerQuestions) {
    events.push(streamEvent({ type: "questions_start", task, questions }));
    userAnswers = await provider.answerQuestions(questions, task);
    events.push(streamEvent({ type: "questions_end", task, questions, userAnswers }));
  }
  if (acquisitionPlans.length > 0 && provider?.executeAcquisitionPlans) {
    events.push(streamEvent({ type: "acquisition_start", task, acquisitionPlans }));
    acquiredEvidenceIds = await provider.executeAcquisitionPlans(acquisitionPlans, task);
    events.push(
      streamEvent({ type: "acquisition_end", task, acquisitionPlans, acquiredEvidenceIds })
    );
  }
  return { userAnswers, acquiredEvidenceIds, events };
}
function streamEvent(event) {
  return { ...event, timestamp: nowIso() };
}
async function startBackendSession(backend, input, context, requestedSessionId) {
  if (backend.start) return backend.start(input, { ...context, requestedSessionId });
  return newRuntimeSession(backend.kind, requestedSessionId);
}
async function resumeBackendSession(backend, session, input, context) {
  if (session.backend !== backend.kind) {
    throw new SessionMismatchError(session.backend, backend.kind);
  }
  if (backend.resume) return backend.resume(session, input, context);
  return touchSession({ ...session, status: "active" });
}
function buildReadiness(task, provider) {
  if (provider?.buildReadiness) return provider.buildReadiness(task);
  return scoreKnowledgeReadiness({
    taskId: task.id,
    requirements: task.requiredKnowledge ?? [],
    metadata: { domain: task.domain, ...task.metadata }
  });
}
function isKnowledgeBlocked(evals) {
  return evals.some((evalResult) => evalResult.id === "knowledge-ready" && !evalResult.passed);
}
function statusFromControl(control) {
  if (control.stoppedBy === "abort") return "aborted";
  if (control.reason.includes("knowledge readiness blocked")) return "blocked";
  if (control.pass) return "completed";
  return "failed";
}
async function emit(sink, event) {
  await sink?.(event);
}
function toAgentContext(task, knowledge, ctx) {
  return {
    task,
    knowledge,
    state: ctx.state,
    evals: ctx.evals,
    history: ctx.history,
    budget: ctx.budget,
    stepIndex: ctx.stepIndex,
    wallMs: ctx.wallMs,
    spentCostUsd: ctx.spentCostUsd,
    remainingCostUsd: ctx.remainingCostUsd,
    abortSignal: ctx.abortSignal
  };
}

// src/runtime-run.ts
function startRuntimeRun(options) {
  if (!options.workspaceId) {
    throw new ValidationError("startRuntimeRun: workspaceId is required");
  }
  if (!options.taskSpec?.id) {
    throw new ValidationError("startRuntimeRun: taskSpec.id is required");
  }
  const now = options.now ?? Date.now;
  const startedAtMs = now();
  const startedAt = new Date(startedAtMs).toISOString();
  const id = options.id ?? `${options.taskSpec.id}:${randomSuffix()}`;
  let status = "running";
  let completedAtMs;
  let resultSummary;
  let error;
  let completionMetadata;
  const ledger = {
    tokensIn: 0,
    tokensOut: 0,
    costUsd: 0,
    wallMs: 0,
    llmCalls: 0
  };
  const snapshotCost = () => ({
    tokensIn: ledger.tokensIn,
    tokensOut: ledger.tokensOut,
    costUsd: ledger.costUsd,
    wallMs: (completedAtMs ?? now()) - startedAtMs,
    llmCalls: ledger.llmCalls
  });
  const buildRow = (extraMetadata) => ({
    id,
    workspaceId: options.workspaceId,
    sessionId: options.sessionId,
    agentId: options.agentId,
    domain: options.taskSpec.domain,
    taskId: options.taskSpec.id,
    scenarioId: options.scenarioId,
    status,
    resultSummary,
    error,
    cost: snapshotCost(),
    startedAt,
    completedAt: completedAtMs !== void 0 ? new Date(completedAtMs).toISOString() : void 0,
    metadata: mergeMetadata(completionMetadata, extraMetadata)
  });
  return {
    id,
    workspaceId: options.workspaceId,
    sessionId: options.sessionId,
    taskSpec: options.taskSpec,
    get status() {
      return status;
    },
    observe(event) {
      if (event.type !== "llm_call") return;
      ledger.llmCalls += 1;
      if (typeof event.tokensIn === "number" && Number.isFinite(event.tokensIn)) {
        ledger.tokensIn += event.tokensIn;
      }
      if (typeof event.tokensOut === "number" && Number.isFinite(event.tokensOut)) {
        ledger.tokensOut += event.tokensOut;
      }
      if (typeof event.costUsd === "number" && Number.isFinite(event.costUsd)) {
        ledger.costUsd += event.costUsd;
      }
    },
    cost: snapshotCost,
    complete(input) {
      if (input.status === "running") {
        throw new ValidationError('complete() requires a terminal status, got "running"');
      }
      if (status !== "running") {
        if (status === input.status) return;
        throw new RuntimeRunStateError(
          `Cannot transition runtime run from "${status}" to "${input.status}"`
        );
      }
      status = input.status;
      completedAtMs = now();
      resultSummary = input.resultSummary;
      error = input.error;
      completionMetadata = input.metadata;
      if (input.cost) {
        if (typeof input.cost.tokensIn === "number" && Number.isFinite(input.cost.tokensIn)) {
          ledger.tokensIn = input.cost.tokensIn;
        }
        if (typeof input.cost.tokensOut === "number" && Number.isFinite(input.cost.tokensOut)) {
          ledger.tokensOut = input.cost.tokensOut;
        }
        if (typeof input.cost.costUsd === "number" && Number.isFinite(input.cost.costUsd)) {
          ledger.costUsd = input.cost.costUsd;
        }
        if (typeof input.cost.llmCalls === "number" && Number.isFinite(input.cost.llmCalls)) {
          ledger.llmCalls = input.cost.llmCalls;
        }
      }
    },
    toRow(metadata) {
      return buildRow(metadata);
    },
    async persist(metadata) {
      if (status === "running") {
        throw new RuntimeRunStateError("Cannot persist a runtime run before complete() is called");
      }
      if (!options.adapter) return;
      await options.adapter.upsert(buildRow(metadata));
    }
  };
}
function mergeMetadata(base, extra) {
  if (!base && !extra) return void 0;
  return { ...base ?? {}, ...extra ?? {} };
}
function randomSuffix() {
  return Math.random().toString(36).slice(2, 10);
}

// src/sse.ts
function encodeServerSentEvent(data, options = {}) {
  const lines = [];
  if (options.id) lines.push(`id: ${stripNewlines(options.id)}`);
  if (options.event) lines.push(`event: ${stripNewlines(options.event)}`);
  if (typeof options.retry === "number" && Number.isFinite(options.retry) && options.retry >= 0) {
    lines.push(`retry: ${Math.floor(options.retry)}`);
  }
  const payload = typeof data === "string" ? data : JSON.stringify(data);
  for (const line of payload.split(/\r?\n/)) {
    lines.push(`data: ${line}`);
  }
  return `${lines.join("\n")}

`;
}
function readinessServerSentEvent(report, options = {}) {
  const { event, id, retry, ...telemetryOptions } = options;
  return encodeServerSentEvent(
    {
      type: "readiness",
      readiness: sanitizeKnowledgeReadinessReport(report, telemetryOptions)
    },
    { event, id, retry }
  );
}
function runtimeStreamServerSentEvent(event, options = {}) {
  const { event: sseEvent, id, retry, ...telemetryOptions } = options;
  return encodeServerSentEvent(sanitizeRuntimeStreamEvent(event, telemetryOptions), {
    event: sseEvent,
    id,
    retry
  });
}
function stripNewlines(value) {
  return value.replace(/[\r\n]/g, " ");
}

// src/tool-loop.ts
var RUNAWAY_BACKSTOP_TURNS = 200;
var DEFAULT_DECISION_CONTEXT_CHARS = 12e3;
var FAILURE_RECOVERY_ACTIONS = ["retry", "verify", "continue", "stop"];
var STUCK_LOOP_THRESHOLD = 3;
function toolCallId(call) {
  return call.toolCallId ?? `call_${call.toolName}`;
}
function assistantToolCallMessage(turnText, pending) {
  return {
    role: "assistant",
    content: turnText.trim() || null,
    tool_calls: pending.map((call) => ({
      id: toolCallId(call),
      type: "function",
      function: { name: call.toolName, arguments: JSON.stringify(call.args) }
    }))
  };
}
function toolResultMessage(call, content) {
  return { role: "tool", tool_call_id: toolCallId(call), content };
}
function defaultRender(label, outcome) {
  if (outcome.ok) return `- ${label} \u2192 ok: ${JSON.stringify(outcome.result)}`;
  return `- ${label} \u2192 failed (${outcome.code}): ${outcome.message}`;
}
async function runToolLoop(opts) {
  const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS;
  const render = opts.renderResult ?? defaultRender;
  const labelFor = opts.labelFor ?? ((c) => c.toolName);
  const runId = opts.runId ?? `agent-run-${randomSuffix2()}`;
  const messages = [
    { role: "system", content: opts.systemPrompt },
    ...opts.priorMessages ?? [],
    { role: "user", content: opts.userMessage }
  ];
  const observer = createToolLoopObserver(opts.hooks, runId, opts.scenarioId);
  const toolResults = [];
  let finalText = "";
  let turns = 0;
  let accumulatedCostUsd = 0;
  let lastCallHash = null;
  let consecutiveCount = 0;
  observer.loopBefore(backstop, messages.length);
  for (let toolTurn = 0; ; toolTurn++) {
    turns++;
    if (opts.deadlineMs !== void 0 && Date.now() >= opts.deadlineMs) {
      observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: "deadline" });
      return { finalText, toolResults, turns, stopReason: "deadline", cappedOut: true };
    }
    let turnText = "";
    const pending = [];
    const turnEventId = observer.turnBefore(toolTurn, messages.length);
    for await (const ev of opts.streamTurn([...messages])) {
      if (ev.type === "text") {
        turnText += ev.text;
        finalText += ev.text;
      } else if (ev.type === "tool_call" && opts.isExecutableTool(ev.call.toolName)) {
        pending.push(ev.call);
      }
    }
    if (pending.length === 0) {
      observer.turnAfter(toolTurn, turnEventId, {
        pendingToolCalls: 0,
        finalTextChars: finalText.length
      });
      break;
    }
    if (toolTurn >= backstop) {
      observer.turnAfter(toolTurn, turnEventId, {
        pendingToolCalls: pending.length,
        stopReason: "backstop"
      });
      observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: "backstop" });
      return { finalText, toolResults, turns, stopReason: "backstop", cappedOut: true };
    }
    messages.push(assistantToolCallMessage(turnText, pending));
    const outcomes = [];
    for (const [callIndex, call] of pending.entries()) {
      const callHash = canonicalCallHash(call);
      if (callHash === lastCallHash) {
        consecutiveCount++;
      } else {
        lastCallHash = callHash;
        consecutiveCount = 1;
      }
      if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {
        observer.turnAfter(toolTurn, turnEventId, {
          pendingToolCalls: pending.length,
          stopReason: "stuck-loop"
        });
        observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: "stuck-loop" });
        return { finalText, toolResults, turns, stopReason: "stuck-loop", cappedOut: true };
      }
      const callEventId = observer.toolCallBefore(toolTurn, turnEventId, callIndex, call);
      let outcome;
      try {
        outcome = await opts.executeToolCall(call);
      } catch (err) {
        outcome = {
          ok: false,
          code: "executor_error",
          message: err instanceof Error ? err.message : String(err)
        };
      }
      if (opts.maxCostUsd !== void 0 && opts.costOf !== void 0) {
        accumulatedCostUsd += opts.costOf(call, outcome);
        if (accumulatedCostUsd >= opts.maxCostUsd) {
          const label2 = labelFor(call);
          toolResults.push({ call, label: label2, outcome });
          messages.push(toolResultMessage(call, render(label2, outcome)));
          observer.toolCallAfter(toolTurn, callEventId, call, outcome);
          observer.turnAfter(toolTurn, turnEventId, {
            pendingToolCalls: pending.length,
            stopReason: "budget"
          });
          observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: "budget" });
          return { finalText, toolResults, turns, stopReason: "budget", cappedOut: true };
        }
      }
      const label = labelFor(call);
      const rendered = render(label, outcome);
      toolResults.push({ call, label, outcome });
      outcomes.push({ call, label, outcome, rendered });
      messages.push(toolResultMessage(call, rendered));
      observer.toolCallAfter(toolTurn, callEventId, call, outcome);
    }
    observer.failureRecovery({
      toolTurn,
      messages,
      turnText,
      outcomes
    });
    observer.turnAfter(toolTurn, turnEventId, {
      pendingToolCalls: pending.length,
      toolResults: outcomes.map((item) => ({
        toolName: item.call.toolName,
        toolCallId: item.call.toolCallId,
        ok: item.outcome.ok
      })),
      failedToolCalls: outcomes.filter((item) => !item.outcome.ok).length
    });
  }
  observer.loopAfter({ turns, toolResults: toolResults.length, stopReason: "completed" });
  return { finalText, toolResults, turns, stopReason: "completed", cappedOut: false };
}
async function* streamToolLoop(opts) {
  const backstop = opts.maxToolTurns ?? RUNAWAY_BACKSTOP_TURNS;
  const render = opts.renderResult ?? defaultRender;
  const labelFor = opts.labelFor ?? ((c) => c.toolName);
  const runId = opts.runId ?? `agent-run-${randomSuffix2()}`;
  const messages = [
    { role: "system", content: opts.systemPrompt },
    ...opts.priorMessages ?? [],
    { role: "user", content: opts.userMessage }
  ];
  const observer = createToolLoopObserver(opts.hooks, runId, opts.scenarioId);
  let accumulatedCostUsd = 0;
  let lastCallHash = null;
  let consecutiveCount = 0;
  observer.loopBefore(backstop, messages.length);
  for (let toolTurn = 0; ; toolTurn++) {
    if (opts.deadlineMs !== void 0 && Date.now() >= opts.deadlineMs) {
      observer.loopAfter({ turns: toolTurn + 1, stopReason: "deadline" });
      yield { kind: "capped", pending: 0, stopReason: "deadline" };
      return;
    }
    let turnText = "";
    const pending = [];
    const turnEventId = observer.turnBefore(toolTurn, messages.length);
    for await (const event of opts.streamTurn([...messages])) {
      yield { kind: "event", event };
      turnText += opts.extractText(event);
      const call = opts.extractToolCall(event);
      if (call && opts.isExecutableTool(call.toolName)) pending.push(call);
    }
    if (pending.length === 0) {
      observer.turnAfter(toolTurn, turnEventId, { pendingToolCalls: 0 });
      observer.loopAfter({ turns: toolTurn + 1, stopReason: "completed" });
      return;
    }
    if (toolTurn >= backstop) {
      observer.turnAfter(toolTurn, turnEventId, {
        pendingToolCalls: pending.length,
        stopReason: "backstop"
      });
      observer.loopAfter({ turns: toolTurn + 1, stopReason: "backstop" });
      yield { kind: "capped", pending: pending.length, stopReason: "backstop" };
      return;
    }
    messages.push(assistantToolCallMessage(turnText, pending));
    const outcomes = [];
    for (const [callIndex, call] of pending.entries()) {
      const callHash = canonicalCallHash(call);
      if (callHash === lastCallHash) {
        consecutiveCount++;
      } else {
        lastCallHash = callHash;
        consecutiveCount = 1;
      }
      if (consecutiveCount >= STUCK_LOOP_THRESHOLD) {
        observer.turnAfter(toolTurn, turnEventId, {
          pendingToolCalls: pending.length,
          stopReason: "stuck-loop"
        });
        observer.loopAfter({ turns: toolTurn + 1, stopReason: "stuck-loop" });
        yield { kind: "capped", pending: pending.length, stopReason: "stuck-loop" };
        return;
      }
      const callEventId = observer.toolCallBefore(toolTurn, turnEventId, callIndex, call);
      let outcome;
      try {
        outcome = await opts.executeToolCall(call);
      } catch (err) {
        outcome = {
          ok: false,
          code: "executor_error",
          message: err instanceof Error ? err.message : String(err)
        };
      }
      if (opts.maxCostUsd !== void 0 && opts.costOf !== void 0) {
        accumulatedCostUsd += opts.costOf(call, outcome);
        if (accumulatedCostUsd >= opts.maxCostUsd) {
          const label2 = labelFor(call);
          yield {
            kind: "tool_result",
            toolName: call.toolName,
            toolCallId: call.toolCallId,
            label: label2,
            outcome
          };
          messages.push(toolResultMessage(call, render(label2, outcome)));
          observer.toolCallAfter(toolTurn, callEventId, call, outcome);
          observer.turnAfter(toolTurn, turnEventId, {
            pendingToolCalls: pending.length,
            stopReason: "budget"
          });
          observer.loopAfter({ turns: toolTurn + 1, stopReason: "budget" });
          yield { kind: "capped", pending: pending.length, stopReason: "budget" };
          return;
        }
      }
      const label = labelFor(call);
      yield {
        kind: "tool_result",
        toolName: call.toolName,
        toolCallId: call.toolCallId,
        label,
        outcome
      };
      const rendered = render(label, outcome);
      outcomes.push({ call, label, outcome, rendered });
      messages.push(toolResultMessage(call, rendered));
      observer.toolCallAfter(toolTurn, callEventId, call, outcome);
    }
    observer.failureRecovery({
      toolTurn,
      messages,
      turnText,
      outcomes
    });
    observer.turnAfter(toolTurn, turnEventId, {
      pendingToolCalls: pending.length,
      toolResults: outcomes.map((item) => ({
        toolName: item.call.toolName,
        toolCallId: item.call.toolCallId,
        ok: item.outcome.ok
      })),
      failedToolCalls: outcomes.filter((item) => !item.outcome.ok).length
    });
  }
}
function createToolLoopObserver(hooks, runId, scenarioId) {
  const loopEventId = `${runId}:agent.run`;
  return {
    loopBefore: (maxToolTurns, messageCount) => {
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.run",
        phase: "before",
        id: `${loopEventId}:before`,
        payload: { maxToolTurns, messageCount }
      });
    },
    loopAfter: (payload) => {
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.run",
        phase: "after",
        id: `${loopEventId}:after`,
        payload
      });
    },
    turnBefore: (toolTurn, messageCount) => {
      const turnEventId = `${loopEventId}:${toolTurn}`;
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.turn",
        phase: "before",
        id: turnEventId,
        stepIndex: toolTurn,
        parentId: loopEventId,
        payload: { messageCount }
      });
      return turnEventId;
    },
    turnAfter: (toolTurn, turnEventId, payload) => {
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.turn",
        phase: "after",
        id: `${turnEventId}:after`,
        stepIndex: toolTurn,
        parentId: turnEventId,
        payload
      });
    },
    toolCallBefore: (toolTurn, turnEventId, callIndex, call) => {
      const callEventId = `${turnEventId}:tool-call:${callIndex}`;
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.tool_call",
        phase: "before",
        id: callEventId,
        stepIndex: toolTurn,
        parentId: turnEventId,
        payload: toolCallPayload(call)
      });
      return callEventId;
    },
    toolCallAfter: (toolTurn, callEventId, call, outcome) => {
      notifyToolLoopEvent({
        hooks,
        runId,
        scenarioId,
        target: "agent.tool_call",
        phase: "after",
        id: `${callEventId}:after`,
        stepIndex: toolTurn,
        parentId: callEventId,
        payload: { ...toolCallPayload(call), outcome: outcomePayload(outcome) }
      });
    },
    failureRecovery: (options) => {
      notifyToolFailureRecovery({
        hooks,
        runId,
        scenarioId,
        stepIndex: options.toolTurn,
        messages: options.messages,
        turnText: options.turnText,
        outcomes: options.outcomes
      });
    }
  };
}
function notifyToolLoopEvent(options) {
  notifyRuntimeHookEvent(options.hooks, {
    id: options.id ?? `${options.runId}:${options.target}:${options.phase}`,
    runId: options.runId,
    scenarioId: options.scenarioId,
    target: options.target,
    phase: options.phase,
    timestamp: Date.now(),
    stepIndex: options.stepIndex,
    parentId: options.parentId,
    payload: options.payload,
    metadata: { producer: "tool-loop", ...options.metadata }
  });
}
function notifyToolFailureRecovery(options) {
  const failed = options.outcomes.filter((item) => !item.outcome.ok);
  if (failed.length === 0) return;
  const evidence = [];
  for (const item of failed) {
    const id = item.call.toolCallId ?? `${options.stepIndex}:${item.label}`;
    evidence.push({
      source: "tool_call",
      id,
      detail: `${item.call.toolName} ${stringifySafe(item.call.args, 2e3)}`,
      metadata: { toolName: item.call.toolName, label: item.label }
    });
    evidence.push({
      source: "tool_result",
      id: `${id}:result`,
      detail: item.rendered,
      metadata: failureMetadata(item.outcome)
    });
  }
  notifyRuntimeDecisionPoint(options.hooks, {
    id: `${options.runId}:agent.turn:${options.stepIndex}:failure-recovery`,
    runId: options.runId,
    scenarioId: options.scenarioId,
    stepIndex: options.stepIndex,
    kind: "retry",
    candidateActions: [...FAILURE_RECOVERY_ACTIONS],
    context: renderDecisionContext(options.messages, options.turnText, options.outcomes),
    evidence,
    metadata: {
      target: "failure-recovery",
      source: "agent.turn",
      failedToolCount: failed.length,
      toolNames: failed.map((item) => item.call.toolName)
    }
  });
}
function toolCallPayload(call) {
  return {
    toolName: call.toolName,
    toolCallId: call.toolCallId,
    argsPreview: stringifySafe(call.args, 2e3)
  };
}
function outcomePayload(outcome) {
  if (!outcome.ok) {
    return {
      ok: false,
      code: outcome.code,
      message: trimText(outcome.message, 2e3),
      status: outcome.status
    };
  }
  return {
    ok: true,
    resultPreview: stringifySafe(outcome.result, 2e3)
  };
}
function failureMetadata(outcome) {
  if (outcome.ok) return void 0;
  return {
    code: outcome.code,
    message: outcome.message,
    status: outcome.status
  };
}
function renderDecisionContext(messages, turnText, outcomes) {
  const recent = messages.slice(-6).map((message) => `[${message.role}]
${message.content ?? ""}`);
  const assistant = turnText.trim() ? [`[assistant]
${turnText}`] : [];
  const toolResults = [`[tool results]
${outcomes.map((item) => item.rendered).join("\n")}`];
  return trimText(
    [...recent, ...assistant, ...toolResults].join("\n\n"),
    DEFAULT_DECISION_CONTEXT_CHARS
  );
}
function canonicalCallHash(call) {
  const sortedArgs = Object.fromEntries(
    Object.entries(call.args).sort(([a], [b]) => a.localeCompare(b))
  );
  return `${call.toolName}:${JSON.stringify(sortedArgs)}`;
}
function stringifySafe(value, max) {
  let text;
  try {
    text = JSON.stringify(value) ?? String(value);
  } catch {
    text = String(value);
  }
  return trimText(text, max);
}
function trimText(text, max) {
  if (text.length <= max) return text;
  return `${text.slice(0, max)}\u2026`;
}
function randomSuffix2(len = 8) {
  return Math.random().toString(36).slice(2, 2 + len);
}
export {
  AGENTIC_PROFILE_RESOURCE_ROOT,
  AgentEvalError,
  BackendTransportError,
  CANDIDATE_TRACE_ENV,
  CANDIDATE_TRACE_TAGS,
  CircuitBreakerState,
  CircuitOpenError,
  ConfigError,
  DEFAULT_MAX_DEPTH,
  DEFAULT_ROUTER_BASE_URL,
  DELEGATED_LOOP_MODES,
  DeadlineExceededError,
  FORWARD_HEADERS,
  FileAgentCandidateExecutionClaimStore,
  FileConversationJournal,
  INTELLIGENCE_WIRE_VERSION,
  InMemoryAgentCandidateExecutionClaimStore,
  InMemoryConversationJournal,
  InMemoryRuntimeSessionStore,
  JudgeError,
  NotFoundError,
  PlannerError,
  RESEARCH_SUPERVISOR_SYSTEM_PROMPT,
  RuntimeRunStateError,
  SqlConversationJournal,
  ValidationError,
  agenticGenerator,
  applyExactAgentProfileDiff,
  applyImprovementWinnerToProfile,
  applyRunRecordDefaults,
  auditLoopRunner,
  buildAgentCandidateBundle,
  buildForwardHeaders,
  buildLoopOtelSpans,
  buildLoopSpanNodes,
  buildRuntimeEventOtelSpans,
  candidateExecutionClaim,
  captureAgentCandidateWorkspace,
  captureAgentCandidateWorkspaceFiles,
  cleanModelId,
  commandVerifier,
  composeRuntimeHooks,
  computeBackoff,
  createAgentCandidateWorkspacePort,
  createAgentKnowledgeReadinessCheck,
  createConversationBackend,
  createIterableBackend,
  createOpenAICompatibleBackend,
  createOtelExporter,
  createProtectedAgentCandidateModelPort,
  createRuntimeEventCollector,
  createRuntimeStreamEventCollector,
  createSandboxPromptBackend,
  createSupervisedKnowledgeUpdater,
  d1ToSqlAdapter,
  decideKnowledgeReadiness,
  defaultIsRetryable,
  defineConversation,
  defineRuntimeHooks,
  deriveExecutionId,
  disposePreparedAgentCandidateExecution,
  executePreparedAgentCandidate,
  exportEvalRuns,
  formatSupervisedKnowledgeTask,
  getModels,
  handleChatTurn,
  improve,
  improvementDriver,
  isDelegatedLoopMode,
  isDepthExceeded,
  knowledgeReadinessDeliverable,
  loopEventToOtelSpan,
  makePerAttemptSignal,
  mcpBuildPrompt,
  mcpServeVerifier,
  mcpToolsForRuntimeMcp,
  mcpToolsForRuntimeMcpSubset,
  notifyRuntimeDecisionPoint,
  notifyRuntimeHookEvent,
  parseExactAgentProfile,
  parseExactAgentProfileDiff,
  parseLoopRunnerArgv,
  persistCandidateOutputArtifact,
  prepareAgentCandidateExecution,
  profileDiffProposer,
  rawTraceDistiller,
  readDepth,
  readinessServerSentEvent,
  recoverExpiredAgentCandidateExecution,
  reflectiveGenerator,
  researchLoopRunner,
  resolveAgentBackend,
  resolveChatModel,
  resolveRouterBaseUrl,
  runAgentTask,
  runAgentTaskStream,
  runConversation,
  runConversationStream,
  runDelegatedLoop,
  runKnowledgeImprovementJob,
  runLoopRunnerCli,
  runPersonaConversation,
  runPersonaDispatch,
  runSupervisedKnowledgeUpdate,
  runToolLoop,
  runtimeStreamServerSentEvent,
  sanitizeAgentRuntimeEvent,
  sanitizeKnowledgeReadinessReport,
  sanitizeRuntimeStreamEvent,
  sealAgentCandidateBundle,
  selfImproveLoopRunner,
  sleep,
  slugifySpeaker,
  startRuntimeRun,
  streamToolLoop,
  toolBuildPrompt,
  turnId,
  validateChatModelId,
  verifyAgentCandidateBundle,
  worktreeLoopRunner
};
//# sourceMappingURL=index.js.map