UNPKG

consortium

Version:

Remote control and session sharing CLI for AI coding agents

1,428 lines • 56.4 kB
'use strict';

var ink = require('ink');
var React = require('react');
var node_crypto = require('node:crypto');
var path = require('node:path');
var os = require('node:os');
var fs = require('node:fs/promises');
var axios = require('axios');
var persistence = require('./types-BYXHizYk.cjs');
var createSessionMetadata = require('./createSessionMetadata-DbNQwTyC.cjs');
var index = require('./index-BXjnng_s.cjs');
var setupOfflineReconnection = require('./setupOfflineReconnection-DhiemrMG.cjs');
var optionsParser = require('./optionsParser-eyp1ynVN.cjs');
var capabilities = require('./capabilities-CxusIo6o.cjs');
var cliRouting = require('./cliRouting-BqxG-6u3.cjs');
var BasePermissionHandler = require('./BasePermissionHandler-DG8EAV5r.cjs');
var resolveModelFromMeta = require('./resolveModelFromMeta-CyeSH3Aa.cjs');
require('chalk');
require('fs');
require('node:fs');
require('node:events');
require('socket.io-client');
require('zod');
require('tweetnacl');
require('node:module');
require('child_process');
require('util');
require('fs/promises');
require('os');
require('crypto');
require('path');
require('url');
require('node:child_process');
require('node:util');
require('http');
require('net');
require('expo-server-sdk');
require('node:net');
require('node:readline');
require('node:url');
require('node:http');
require('node:https');
require('node:stream/promises');
require('tar');
require('ps-list');
require('cross-spawn');
require('tmp');
require('qrcode-terminal');
require('open');
require('fastify');
require('fastify-type-provider-zod');
require('ws');
require('playwright-core');
require('imapflow');
require('mailparser');
require('yaml');
require('@modelcontextprotocol/sdk/client/index.js');
require('@modelcontextprotocol/sdk/client/streamableHttp.js');
require('readline');
require('@modelcontextprotocol/sdk/server/mcp.js');
require('@modelcontextprotocol/sdk/server/streamableHttp.js');
require('libsodium-wrappers');
require('pixelmatch');
require('pngjs');
require('@agentclientprotocol/sdk');

const CONSORTIUM_CODE_TIMEOUTS = {
  /** OpenCode boots fast, but first run performs a SQLite migration. 60s is safe. */
  init: 6e4,
  /** Standard tool call timeout */
  toolCall: 12e4,
  /** Idle detection after last message chunk */
  idle: 500
};
const STDERR_NOISE_PATTERNS = [
  "Performing one time database migration",
  "sqlite-migration:done",
  "Database migration complete",
  "Shell cwd was reset"
];
const UNSUPPORTED_SESSION_UPDATE_VARIANTS = /* @__PURE__ */ new Set([
  "usage_update",
  "available_commands_update"
]);
class ConsortiumCodeTransport {
  agentName = "consortium-code";
  getInitTimeout() {
    return CONSORTIUM_CODE_TIMEOUTS.init;
  }
  getIdleTimeout() {
    return CONSORTIUM_CODE_TIMEOUTS.idle;
  }
  getToolCallTimeout() {
    return CONSORTIUM_CODE_TIMEOUTS.toolCall;
  }
  /**
   * Defensive stdout filter. With `--log-level ERROR --pure` OpenCode's stdout
   * is clean ACP JSON-RPC, but we still drop any non-JSON lines as a safety net
   * (matches the pattern used by GeminiTransport).
   *
   * We also drop OpenCode-specific notification variants that the
   * @agentclientprotocol/sdk validator doesn't know about — currently this is
   * `session/update` notifications with `sessionUpdate: 'usage_update'` (token
   * cost / context size info). Letting these reach the SDK causes a noisy
   * `Error handling notification` log without affecting functionality. We drop
   * them at the transport layer so the SDK never sees them.
   */
  filterStdoutLine(line) {
    const trimmed = line.trim();
    if (!trimmed) {
      return null;
    }
    if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
      return null;
    }
    let parsed;
    try {
      parsed = JSON.parse(trimmed);
    } catch {
      return null;
    }
    if (typeof parsed !== "object" || parsed === null) {
      return null;
    }
    const msg = parsed;
    if (msg.method === "session/update") {
      const variant = msg.params?.update?.sessionUpdate;
      if (variant && UNSUPPORTED_SESSION_UPDATE_VARIANTS.has(variant)) {
        return null;
      }
    }
    return line;
  }
  /**
   * Handle OpenCode stderr output.
   *
   * - Migration / shutdown lifecycle messages: suppress entirely
   * - Upstream provider rate limits (429): suppress (OpenCode handles retries)
   * - 401 / auth errors: surface as a user-visible error
   * - Everything else: pass through to logs without emitting a UI message
   */
  handleStderr(text, _context) {
    const trimmed = text.trim();
    if (!trimmed) {
      return { message: null, suppress: true };
    }
    for (const pattern of STDERR_NOISE_PATTERNS) {
      if (trimmed.includes(pattern)) {
        return { message: null, suppress: true };
      }
    }
    if (trimmed.includes("status 429") || trimmed.includes('"code":429') || trimmed.includes("rate limit") || trimmed.includes("Rate limit")) {
      return { message: null, suppress: false };
    }
    if (trimmed.includes("status 401") || trimmed.includes('"code":401') || trimmed.includes("Unauthorized") || trimmed.includes("authentication required")) {
      const errorMessage = {
        type: "status",
        status: "error",
        detail: "Consortium Code authentication failed. Run `opencode auth login` to configure a provider, or set OPENAI_BASE_URL to point at the Consortium proxy."
      };
      return { message: errorMessage };
    }
    return { message: null };
  }
  /**
   * Tool patterns. Empty for now — OpenCode's tool IDs appear to follow the
   * `tool_name-<id>` convention, so the default extractor in AcpBackend
   * handles the common case. Add specific patterns here as we observe tools
   * that need disambiguation.
   */
  getToolPatterns() {
    return [];
  }
}
const consortiumCodeTransport = new ConsortiumCodeTransport();

function redactToken(token) {
  if (!token) return "(none)";
  if (token.length <= 8) return "***";
  return token.slice(0, 4) + "..." + token.slice(-4);
}
function validateNoTokensInArgs(args) {
  const sensitivePatterns = [
    /^(sk-|pk-|key-|token-)/i,
    /^eyJ/
    // JWT prefix
  ];
  for (const arg of args) {
    for (const pattern of sensitivePatterns) {
      if (pattern.test(arg)) {
        throw new Error(
          "Security: sensitive token detected in command arguments. Tokens must be passed via environment variables, not CLI args."
        );
      }
    }
  }
}
const LOGGABLE_ENV = [
  /^PATH$/,
  /^HOME$/,
  /^SHELL$/,
  /^TERM(_PROGRAM)?$/,
  /^LANG$/,
  /^LC_/,
  /^TZ$/,
  /^NODE_ENV$/,
  /^PWD$/,
  /^USER$/,
  /^TMPDIR$/,
  /^CONSORTIUM_(SERVER_URL|HOME_DIR|SKIP_TUI|DEFAULT_AGENT)$/
];
function looksSecret(value) {
  if (/^(sk-|pk-|key-|token-|eyJ)/i.test(value)) return true;
  if (/[A-Za-z0-9+/_=-]{32,}/.test(value) && !/\s/.test(value.trim())) return true;
  return false;
}
function safeEnvForLogging(env) {
  const safe = {};
  for (const [key, value] of Object.entries(env)) {
    const loggable = LOGGABLE_ENV.some((re) => re.test(key)) && !looksSecret(value);
    safe[key] = loggable ? value : redactToken(value);
  }
  return safe;
}

function isDirectZenModel(model) {
  return model.startsWith("opencode/");
}
function createConsortiumCodeBackend(options) {
  const pure = options.pure ?? true;
  const logLevel = options.logLevel ?? "ERROR";
  const useProxy = !!(options.proxyUrl && options.proxyTokens?.consortium);
  const consortiumMode = options.consortiumMode ?? useProxy;
  let command;
  try {
    command = cliRouting.resolveConsortiumCodeBinarySync();
  } catch {
    persistence.logger.debug("[ConsortiumCode] Failed to resolve bundled binary, falling back to PATH lookup");
    command = consortiumMode ? "consortium-code" : "opencode";
  }
  const args = [
    "acp",
    "--log-level",
    logLevel,
    "--port",
    "0",
    "--cwd",
    options.cwd
  ];
  if (pure) args.push("--pure");
  if (consortiumMode) args.push("--consortium-mode");
  validateNoTokensInArgs(args);
  const env = {
    ...options.env
  };
  const selectedModel = options.model ?? "opencode/big-pickle";
  env.OPENCODE_MODEL = selectedModel;
  const directZen = isDirectZenModel(selectedModel);
  if (useProxy) {
    const configuredModel = selectedModel.includes("/") ? selectedModel : `opencode/${selectedModel}`;
    const tokens = options.proxyTokens;
    const provider = {};
    if (!directZen) {
      provider.opencode = { options: { baseURL: `${options.proxyUrl}/v1/proxy/consortium`, apiKey: tokens.consortium } };
    }
    if (tokens.anthropic) provider.anthropic = { options: { baseURL: `${options.proxyUrl}/v1/proxy/anthropic`, apiKey: tokens.anthropic } };
    if (tokens.openai) provider.openai = { options: { baseURL: `${options.proxyUrl}/v1/proxy/openai`, apiKey: tokens.openai } };
    if (tokens.google) provider.google = { options: { baseURL: `${options.proxyUrl}/v1/proxy/google`, apiKey: tokens.google } };
    if (tokens.zai) provider.zai = { options: { baseURL: `${options.proxyUrl}/v1/proxy/zai`, apiKey: tokens.zai } };
    env.OPENCODE_CONFIG_CONTENT = JSON.stringify({ model: configuredModel, provider });
  }
  if (useProxy && !directZen) {
    env.CONSORTIUM_PROXY_URL = `${options.proxyUrl}/v1/proxy/consortium`;
    env.CONSORTIUM_PROXY_TOKEN = options.proxyTokens.consortium;
  }
  if (options.baseUrl) {
    env.OPENAI_BASE_URL = options.baseUrl;
    env.ANTHROPIC_BASE_URL = options.baseUrl;
  }
  if (options.apiKey) {
    env.OPENAI_API_KEY = options.apiKey;
    env.ANTHROPIC_API_KEY = options.apiKey;
  }
  const billingMode = directZen ? "direct-zen" : useProxy ? "proxy" : options.apiKey ? "byok" : "zen";
  const backendOptions = {
    agentName: "consortium-code",
    cwd: options.cwd,
    command,
    args,
    env,
    mcpServers: options.mcpServers,
    permissionHandler: options.permissionHandler,
    transportHandler: consortiumCodeTransport
  };
  persistence.logger.debug("[ConsortiumCode] Creating ACP backend:", {
    command,
    billingMode,
    cwd: backendOptions.cwd,
    args: backendOptions.args,
    model: options.model ?? "(default/zen)",
    pure,
    logLevel,
    consortiumMode,
    mcpServerCount: options.mcpServers ? Object.keys(options.mcpServers).length : 0,
    env: safeEnvForLogging(env)
  });
  return new capabilities.AcpBackend(backendOptions);
}

function isWriteLikeTool(toolName) {
  const lower = toolName.toLowerCase();
  if (lower === "other" || lower === "unknown tool" || lower === "unknown") return true;
  const patterns = [
    "edit",
    "write",
    "patch",
    "delete",
    "remove",
    "create",
    "mkdir",
    "rename",
    "move",
    "copy",
    "exec",
    "bash",
    "shell",
    "run",
    "terminal"
  ];
  return patterns.some((p) => lower === p || lower.includes(p));
}
const ALWAYS_AUTO_APPROVE = ["change_title", "think", "save_memory"];
class ConsortiumCodePermissionHandler extends BasePermissionHandler.BasePermissionHandler {
  currentPermissionMode = "default";
  constructor(session) {
    super(session);
  }
  getLogPrefix() {
    return "[ConsortiumCode]";
  }
  updateSession(newSession) {
    super.updateSession(newSession);
  }
  setPermissionMode(mode) {
    this.currentPermissionMode = mode;
    persistence.logger.debug(`${this.getLogPrefix()} Permission mode set to: ${mode}`);
  }
  shouldAutoApprove(toolName, toolCallId) {
    if (ALWAYS_AUTO_APPROVE.some((n) => toolName.toLowerCase().includes(n))) return true;
    if (ALWAYS_AUTO_APPROVE.some((n) => toolCallId.toLowerCase().includes(n))) return true;
    switch (this.currentPermissionMode) {
      case "yolo":
        return true;
      case "safe-yolo":
        return !isWriteLikeTool(toolName);
      case "read-only":
        return !isWriteLikeTool(toolName);
      default:
        return false;
    }
  }
  async handleToolCall(toolCallId, toolName, input) {
    if (this.shouldAutoApprove(toolName, toolCallId)) {
      const decision = this.currentPermissionMode === "yolo" ? "approved_for_session" : "approved";
      persistence.logger.debug(`${this.getLogPrefix()} Auto-approving tool ${toolName} (${toolCallId}) in ${this.currentPermissionMode} mode`);
      this.session.updateAgentState((currentState) => ({
        ...currentState,
        completedRequests: {
          ...currentState.completedRequests,
          [toolCallId]: {
            tool: toolName,
            arguments: input,
            createdAt: Date.now(),
            completedAt: Date.now(),
            status: "approved",
            decision
          }
        }
      }));
      return { decision };
    }
    const defaultOnTimeout = isWriteLikeTool(toolName) ? "denied" : "approved";
    return this.createPendingRequest(toolCallId, toolName, input, { defaultOnTimeout });
  }
}

async function runConsortiumCode(opts) {
  persistence.connectionState.setBackend("ConsortiumCode");
  persistence.logger.debug(`[ConsortiumCode] Starting: startedBy=${opts.startedBy || "terminal"}`);
  let session;
  let permissionHandler;
  let isProcessingMessage = false;
  let pendingSessionSwap = null;
  const applyPendingSessionSwap = () => {
    if (pendingSessionSwap) {
      persistence.logger.debug("[ConsortiumCode] Applying pending session swap");
      session = pendingSessionSwap;
      permissionHandler.updateSession(pendingSessionSwap);
      pendingSessionSwap = null;
    }
  };
  const api = await persistence.ApiClient.create(opts.credentials);
  if (opts.existingSession) {
    session = opts.existingSession;
  } else {
    const sessionTag = opts.resumeTag ?? node_crypto.randomUUID();
    const settings = await persistence.readSettings();
    const machineId = settings?.machineId;
    if (!machineId) {
      console.error("[START] No machine ID found. Run consortium auth to set up.");
      process.exit(1);
    }
    await api.getOrCreateMachine({ machineId, metadata: index.initialMachineMetadata });
    const { state, metadata } = createSessionMetadata.createSessionMetadata({
      flavor: "consortium-code",
      machineId,
      startedBy: opts.startedBy
    });
    const activeOrgId = await index.ensureActiveOrg(opts.credentials);
    const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state, organizationId: activeOrgId ?? void 0, projectId: process.env.CONSORTIUM_PROJECT_ID || void 0 });
    const { session: initialSession } = setupOfflineReconnection.setupOfflineReconnection({
      organizationId: activeOrgId ?? void 0,
      api,
      sessionTag,
      metadata,
      state,
      response,
      onSessionSwap: (newSession) => {
        if (isProcessingMessage) {
          persistence.logger.debug("[ConsortiumCode] Session swap queued (processing in progress)");
          pendingSessionSwap = newSession;
        } else {
          session = newSession;
          permissionHandler.updateSession(newSession);
        }
      }
    });
    session = initialSession;
    if (response) {
      try {
        const result = await index.notifyDaemonSessionStarted(response.id, metadata);
        if (result.error) {
          persistence.logger.debug("[ConsortiumCode] Failed to report to daemon:", result.error);
        }
      } catch (error) {
        persistence.logger.debug("[ConsortiumCode] Failed to report to daemon:", error);
      }
    }
  }
  const consortiumServer = await index.startConsortiumServer(session, {
    sendArtifact: (msg) => session.sendAgentMessage("consortium-code", msg)
  });
  permissionHandler = new ConsortiumCodePermissionHandler(session);
  const driveClient = index.createAttachmentDriveClient(opts.credentials);
  const scratchDir = new index.ScratchDir(session.sessionId, { workspaceDir: process.cwd() });
  const attachmentResolver = driveClient ? new index.AttachmentResolver(driveClient, scratchDir) : null;
  const pendingMessageLocalIds = [];
  const pendingDriveNodeRefs = [];
  const pendingAttachmentIds = [];
  const pendingRecords = [];
  const messageQueue = opts.messageQueue ?? new index.MessageQueue2((mode) => index.hashObject({
    permissionMode: mode.permissionMode,
    model: mode.model
  }));
  session.setQueueStatsProvider(() => messageQueue.stats());
  let currentPermissionMode = void 0;
  let currentModel = void 0;
  let currentBackendModel = void 0;
  let hasStoredFirstMessage = false;
  let isFirstMessage = true;
  session.onUserMessage((message) => {
    const text = message.content.text;
    const env = index.normalizeAttachmentEnvelope(message);
    if (driveClient && env.driveId && env.driveDek) {
      try {
        driveClient.setKnownDek(env.driveId, new Uint8Array(Buffer.from(env.driveDek, "base64")));
      } catch (err) {
        persistence.logger.debug("[ConsortiumCode] setKnownDek failed:", err);
      }
    }
    if (env.messageLocalId) pendingMessageLocalIds.push(env.messageLocalId);
    if (env.driveId && env.driveNodeIds.length > 0) {
      pendingDriveNodeRefs.push({ driveId: env.driveId, nodeIds: env.driveNodeIds });
    }
    if (env.bindingOnlyNodeIds.length > 0) {
      pendingAttachmentIds.push(...env.bindingOnlyNodeIds);
    }
    if (env.records.length > 0) {
      pendingRecords.push(...env.records);
      if (driveClient) {
        for (const rec of env.records) {
          if (!rec.key) continue;
          try {
            driveClient.setKnownNodeKey(rec.nodeId, new Uint8Array(Buffer.from(rec.key, "base64")));
          } catch (err) {
            persistence.logger.debug("[ConsortiumCode] setKnownNodeKey failed:", err);
          }
        }
      }
    }
    if (!hasStoredFirstMessage && text) {
      hasStoredFirstMessage = true;
      session.updateMetadata((m) => ({ ...m, firstMessage: text.substring(0, 100) }));
    }
    if (message.meta?.permissionMode) {
      const validModes = ["default", "read-only", "safe-yolo", "yolo"];
      if (validModes.includes(message.meta.permissionMode)) {
        currentPermissionMode = message.meta.permissionMode;
        permissionHandler.setPermissionMode(currentPermissionMode);
      }
    }
    if (currentPermissionMode === void 0) {
      currentPermissionMode = "default";
      permissionHandler.setPermissionMode("default");
    }
    currentModel = resolveModelFromMeta.resolveModelFromMeta(message.meta, currentModel).model;
    let fullPrompt = text;
    if (isFirstMessage) {
      const systemParts = [index.ARTIFACT_SYSTEM_STEER, index.ATLAS_SYSTEM_STEER];
      if (message.meta?.appendSystemPrompt) systemParts.push(message.meta.appendSystemPrompt);
      fullPrompt = systemParts.join("\n\n") + "\n\n" + text;
    }
    isFirstMessage = false;
    const mode = {
      permissionMode: currentPermissionMode || "default",
      model: currentModel
    };
    const special = index.parseSpecialCommand(text);
    if (special.type === "clear") {
      messageQueue.pushIsolateAndClear(fullPrompt, mode);
    } else {
      messageQueue.push(fullPrompt, mode, message.content.localId);
    }
  });
  const messageBuffer = opts.messageBuffer ?? new index.MessageBuffer();
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
  let inkInstance = null;
  if (hasTTY) {
    console.clear();
    inkInstance = ink.render(React.createElement(index.RemoteModeDisplay, {
      messageBuffer,
      logPath: process.env.DEBUG ? persistence.logger.logFilePath : void 0,
      onExit: async () => {
        shouldExit = true;
        await handleAbort();
      },
      onSwitchToLocal: () => {
        persistence.logger.debug("[ConsortiumCode] Switching to local mode via double-space");
        switchToLocal = true;
        shouldExit = true;
        handleAbort();
      }
    }), { exitOnCtrlC: false, patchConsole: false });
  }
  const agent = "consortium-code";
  let thinking = false;
  let shouldExit = false;
  let switchToLocal = false;
  let abortController = new AbortController();
  let acpSessionId = null;
  let backend = null;
  let currentModeHash = null;
  let accumulatedText = "";
  let turnStartMs = 0;
  let emptyIndicatorTimer = null;
  const EMPTY_INDICATOR_DELAY_MS = 2500;
  const cancelEmptyIndicator = () => {
    if (emptyIndicatorTimer) {
      clearTimeout(emptyIndicatorTimer);
      emptyIndicatorTimer = null;
    }
  };
  let sendPromptInFlight = false;
  const OPTIONS_MARKER = "<options";
  const HOLDBACK_LEN = OPTIONS_MARKER.length - 1;
  let streamBuffer = "";
  let streamFlushTimer = null;
  const STREAM_FLUSH_MS = 600;
  const STREAM_FLUSH_CHARS = 1500;
  let turnFinalized = true;
  let watchdog = null;
  const WATCHDOG_MS = 24e4;
  const clearWatchdog = () => {
    if (watchdog) {
      clearTimeout(watchdog);
      watchdog = null;
    }
  };
  const resetWatchdog = () => {
    clearWatchdog();
    if (!turnFinalized && sendPromptInFlight) {
      watchdog = setTimeout(() => {
        persistence.logger.debug("[ConsortiumCode] turn stalled (no backend activity for 90s); forcing finalize");
        finalizeTurn("watchdog");
      }, WATCHDOG_MS);
    }
  };
  const startTurn = () => {
    turnFinalized = false;
    accumulatedText = "";
    streamBuffer = "";
    turnStartMs = Date.now();
    if (streamFlushTimer) {
      clearTimeout(streamFlushTimer);
      streamFlushTimer = null;
    }
    cancelEmptyIndicator();
    sendPromptInFlight = true;
    resetWatchdog();
  };
  const readLatestOpencodeError = async () => {
    try {
      const logDir = path.join(os.homedir(), ".local/share/opencode/log");
      const entries = await fs.readdir(logDir).catch(() => []);
      let bestPath = null;
      let bestMtime = 0;
      for (const name of entries) {
        if (!name.endsWith(".log")) continue;
        const path$1 = path.join(logDir, name);
        const s = await fs.stat(path$1).catch(() => null);
        if (!s) continue;
        if (s.mtimeMs < turnStartMs - 2e3) continue;
        if (s.mtimeMs > bestMtime) {
          bestMtime = s.mtimeMs;
          bestPath = path$1;
        }
      }
      if (!bestPath) return null;
      const body = await fs.readFile(bestPath, "utf8").catch(() => null);
      if (!body) return null;
      const lines = body.split("\n");
      for (let i = lines.length - 1; i >= 0; i--) {
        const line = lines[i];
        if (!line.includes("ERROR") || !line.includes("service=llm")) continue;
        const msgMatch = line.match(/"message":"((?:[^"\\]|\\.)*)"/);
        const statusMatch = line.match(/"statusCode":(\d+)/);
        const message = msgMatch?.[1]?.replace(/\\"/g, '"').replace(/\\\\/g, "\\") ?? null;
        const status = statusMatch?.[1] ?? null;
        if (message) {
          return status ? `HTTP ${status}: ${message}` : message;
        }
      }
      return null;
    } catch {
      return null;
    }
  };
  const finalizeTurn = (reason) => {
    if (turnFinalized) return;
    turnFinalized = true;
    clearWatchdog();
    flushStreamBuffer();
    try {
      const tail = streamBuffer;
      streamBuffer = "";
      let finalMessage = tail;
      const markerIdx = tail.indexOf(OPTIONS_MARKER);
      if (markerIdx !== -1) {
        const pre = tail.slice(0, markerIdx);
        const rest = tail.slice(markerIdx);
        const { text: optionsText, options } = optionsParser.parseOptionsFromText(rest);
        if (options.length > 0) {
          finalMessage = pre + optionsText + optionsParser.formatOptionsXml(options);
          persistence.logger.debug(`[ConsortiumCode] Found ${options.length} options in response:`, options);
        } else if (optionsParser.hasIncompleteOptions(rest)) {
          persistence.logger.debug("[ConsortiumCode] Warning: Incomplete options block detected");
          finalMessage = pre + rest;
        }
      }
      if (finalMessage.trim()) {
        session.sendAgentMessage(agent, {
          type: "message",
          message: finalMessage,
          id: node_crypto.randomUUID()
        });
      } else if (!accumulatedText.trim() && reason !== "error" && reason !== "watchdog") {
        cancelEmptyIndicator();
        emptyIndicatorTimer = setTimeout(() => {
          emptyIndicatorTimer = null;
          if (accumulatedText.trim().length > 0 || streamBuffer.length > 0) return;
          session.sendAgentMessage(agent, {
            type: "message",
            message: "_(model returned no output \u2014 checking Consortium logs for details\u2026)_",
            id: node_crypto.randomUUID()
          });
          readLatestOpencodeError().then((detail) => {
            if (!detail) return;
            session.sendAgentMessage(agent, {
              type: "message",
              message: `_(model error \u2014 ${detail})_`,
              id: node_crypto.randomUUID()
            });
          }).catch(() => {
          });
        }, EMPTY_INDICATOR_DELAY_MS);
      }
    } catch (e) {
      persistence.logger.debug("[ConsortiumCode] turn finalize flush failed:", e);
    }
    if (streamFlushTimer) {
      clearTimeout(streamFlushTimer);
      streamFlushTimer = null;
    }
    if (accumulatedText.trim()) {
      void session.autoDetectArtifactsFromText(agent, accumulatedText);
    }
    session.sendAgentMessage(agent, { type: "task_complete", id: node_crypto.randomUUID() });
    thinking = false;
    session.keepAlive(thinking, "remote");
    persistence.logger.debug(`[ConsortiumCode] turn finalized via ${reason}`);
  };
  const flushStreamBuffer = () => {
    if (streamFlushTimer) {
      clearTimeout(streamFlushTimer);
      streamFlushTimer = null;
    }
    if (!streamBuffer) return;
    let cutAt = streamBuffer.length;
    const optIdx = streamBuffer.indexOf(OPTIONS_MARKER);
    if (optIdx !== -1) cutAt = optIdx;
    if (cutAt === streamBuffer.length && cutAt > HOLDBACK_LEN) {
      cutAt -= HOLDBACK_LEN;
    }
    const lookbackStart = Math.max(0, cutAt - 400);
    const lastBreak = streamBuffer.lastIndexOf("\n\n", cutAt);
    if (lastBreak >= lookbackStart) cutAt = lastBreak + 2;
    if (cutAt <= 0) return;
    const chunk = streamBuffer.slice(0, cutAt);
    streamBuffer = streamBuffer.slice(cutAt);
    if (!chunk) return;
    session.sendAgentMessage(agent, {
      type: "message",
      message: chunk,
      id: node_crypto.randomUUID()
    });
  };
  const streamModelChunk = (chunk) => {
    if (!chunk) return;
    cancelEmptyIndicator();
    messageBuffer.addMessage(chunk, "assistant");
    resetWatchdog();
    if (sendPromptInFlight) accumulatedText += chunk;
    streamBuffer += chunk;
    if (streamBuffer.length >= STREAM_FLUSH_CHARS) {
      flushStreamBuffer();
    } else if (!streamFlushTimer) {
      streamFlushTimer = setTimeout(flushStreamBuffer, STREAM_FLUSH_MS);
    }
  };
  session.keepAlive(thinking, "remote");
  const keepAliveInterval = setInterval(() => {
    if (!shouldExit) session.keepAlive(thinking, "remote");
  }, 2e3);
  session.updateAgentState((state) => ({ ...state, controlledByUser: false }));
  index.startCaffeinate();
  const sendReady = () => {
    session.sendSessionEvent({ type: "ready" });
    try {
      api.push().sendToAllDevices(
        "It's ready!",
        "Consortium Code is waiting for your command",
        { sessionId: session.sessionId }
      );
    } catch (pushError) {
      persistence.logger.debug("[ConsortiumCode] Failed to send ready push", pushError);
    }
  };
  const spawnModelId = process.env.OPENCODE_MODEL || "opencode/big-pickle";
  const byokProviderEnv = process.env.CONSORTIUM_CODE_BYOK_PROVIDER;
  const byokFlag = process.env.CONSORTIUM_CODE_BYOK === "1";
  function inferProviderFromModel(id) {
    if (id.startsWith("custom:")) {
      const rest = id.slice("custom:".length);
      const ci = rest.indexOf(":");
      return ci > 0 ? `custom:${rest.slice(0, ci)}` : `custom:${rest}`;
    }
    if (id.includes(":")) return id.slice(0, id.indexOf(":"));
    if (id.includes("/")) return id.split("/")[0];
    return void 0;
  }
  const inferredScope = inferProviderFromModel(spawnModelId);
  const byokProviderId = byokProviderEnv ?? (byokFlag && inferredScope && inferredScope !== "opencode" && inferredScope !== "consortium" ? inferredScope : void 0);
  let byokActive = false;
  if (byokProviderId && byokProviderId !== "consortium") {
    const providerEntry = index.getProvider("consortium-code", byokProviderId);
    const opencodeEntry = index.findOpencodeProvider(byokProviderId);
    const envVarFromEnv = process.env.CONSORTIUM_CODE_BYOK_ENV_VAR;
    const envVar = providerEntry?.keyEnvVar ?? opencodeEntry?.env?.[0] ?? envVarFromEnv;
    if (envVar) {
      const existing = process.env[envVar];
      let resolvedKey = existing && existing.length > 0 ? existing : null;
      if (!resolvedKey) {
        try {
          resolvedKey = await index.getProviderKeyViaDaemon(byokProviderId);
        } catch (err) {
          persistence.logger.debug(`[ConsortiumCode] BYOK getProviderKeyViaDaemon(${byokProviderId}) threw:`, err);
        }
      }
      if (resolvedKey) {
        process.env[envVar] = resolvedKey;
        delete process.env.OPENAI_BASE_URL;
        delete process.env.ANTHROPIC_BASE_URL;
        byokActive = true;
        const modelTail = (() => {
          if (spawnModelId.startsWith("custom:")) {
            const rest = spawnModelId.slice("custom:".length);
            const ci = rest.indexOf(":");
            return ci > 0 ? rest.slice(ci + 1) : "";
          }
          if (spawnModelId.includes(":")) return spawnModelId.slice(spawnModelId.indexOf(":") + 1);
          if (spawnModelId.includes("/")) return spawnModelId.split("/").slice(1).join("/");
          return spawnModelId;
        })();
        const opencodeModelId = `${byokProviderId}/${modelTail}`;
        const customBaseURL = process.env.CONSORTIUM_CODE_BYOK_BASE_URL;
        const config = { model: opencodeModelId };
        if (byokProviderId.startsWith("custom:")) {
          if (customBaseURL && modelTail) {
            config.provider = {
              [byokProviderId]: {
                npm: "@ai-sdk/openai-compatible",
                options: { baseURL: customBaseURL, apiKey: resolvedKey },
                models: { [modelTail]: {} }
              }
            };
          }
        } else if (modelTail) {
          config.provider = {
            [byokProviderId]: {
              options: { apiKey: resolvedKey },
              models: { [modelTail]: {} }
            }
          };
        }
        process.env.OPENCODE_CONFIG_CONTENT = JSON.stringify(config);
        persistence.logger.debug(`[ConsortiumCode] BYOK active for provider=${byokProviderId} envVar=${envVar} model=${opencodeModelId}; bypassing Consortium proxy mint.`);
      } else {
        persistence.logger.warn(`[ConsortiumCode] BYOK requested for provider=${byokProviderId} but no key found in env or daemon keystore; falling back to proxy path.`);
      }
    } else {
      persistence.logger.warn(`[ConsortiumCode] BYOK requested for unknown provider=${byokProviderId}; falling back to proxy path.`);
    }
  }
  const isFreeSlug = spawnModelId.startsWith("opencode/") || spawnModelId.startsWith("consortium/");
  const PROVIDER_SCOPES = ["consortium", "anthropic", "openai", "google", "zai"];
  const scopesToMint = isFreeSlug ? ["consortium"] : PROVIDER_SCOPES;
  let consortiumProxyUrl = process.env.CONSORTIUM_PROXY_URL;
  let proxyTokens;
  const extractAxiosError = (reason) => {
    const data = reason?.response?.data;
    const status = reason?.response?.status;
    const fromData = data?.error || data?.message;
    const body = typeof fromData === "string" ? fromData : fromData ? JSON.stringify(fromData) : void 0;
    const base = body || reason?.message || String(reason);
    return status ? `${status} ${base}` : base;
  };
  const extractStructuredError = (reason) => {
    const data = reason?.response?.data;
    const code = typeof data?.error === "string" && data.error || typeof data?.code === "string" && data.code || reason?.code && String(reason.code) || "mint_failed";
    const message = typeof data?.message === "string" && data.message || typeof data?.error === "string" && data.error || reason?.message || String(reason);
    return { code, message };
  };
  const mintResults = byokActive ? [] : await Promise.allSettled(scopesToMint.map((providerId) => axios.post(
    `${persistence.configuration.serverUrl}/v1/billing/proxy-token`,
    { providerId, sessionId: session.sessionId, usageMode: "consortium-code" },
    { headers: { Authorization: `Bearer ${opts.credentials.token}` }, timeout: 1e4 }
  )));
  const failed = [];
  const scopeErrors = {};
  const tokens = {};
  let firstProxyBaseUrl;
  let anyNetworkSucceeded = false;
  mintResults.forEach((result, idx) => {
    const scope = scopesToMint[idx];
    if (result.status === "fulfilled") {
      anyNetworkSucceeded = true;
      const data = result.value.data;
      if (data?.token && data?.proxyBaseUrl) {
        tokens[scope] = data.token;
        if (!firstProxyBaseUrl) {
          firstProxyBaseUrl = String(data.proxyBaseUrl).replace(/\/v1\/proxy\/[^/]+$/, "");
        }
      } else {
        failed.push(`${scope}: empty response`);
        scopeErrors[scope] = { code: "empty_response", message: "Server returned no token" };
      }
    } else {
      failed.push(`${scope}: ${extractAxiosError(result.reason)}`);
      scopeErrors[scope] = extractStructuredError(result.reason);
    }
  });
  if (byokActive) {
    persistence.logger.debug("[ConsortiumCode] BYOK mode \u2014 skipping proxy mint + wallet preview.");
  } else if (failed.length === 0 && tokens.consortium) {
    proxyTokens = tokens;
    if (firstProxyBaseUrl) consortiumProxyUrl = firstProxyBaseUrl;
    persistence.logger.debug(`[ConsortiumCode] Acquired proxy tokens (${scopesToMint.length} provider${scopesToMint.length === 1 ? "" : "s"}) for session ${session.sessionId}`);
    const desiredModel = process.env.OPENCODE_MODEL;
    if (desiredModel) {
      const [scope, ...rest] = desiredModel.split("/");
      const bareModelId = rest.length > 0 ? rest.join("/") : scope;
      const overrideProvider = rest.length === 0 || scope === "opencode" ? "consortium" : scope;
      try {
        await axios.post(
          `${persistence.configuration.serverUrl}/v1/proxy/model`,
          // CC-2 / CONTRACT A: scope the override to THIS session so a
          // spawn-time pick doesn't repaint other live sessions' model pill.
          { providerId: overrideProvider, modelId: bareModelId, sessionId: session.sessionId },
          { headers: { Authorization: `Bearer ${opts.credentials.token}` }, timeout: 5e3 }
        );
        persistence.logger.debug(`[ConsortiumCode] Set proxy model override ${overrideProvider}/${bareModelId} for session ${session.sessionId}`);
      } catch (err) {
        persistence.logger.warn(`[ConsortiumCode] setModelOverride failed (binary may silently fall back to big-pickle): ${err?.message ?? err}`);
      }
    }
  } else if (!anyNetworkSucceeded) {
    const envTokens = {
      consortium: process.env.CONSORTIUM_PROXY_TOKEN_CONSORTIUM ?? process.env.CONSORTIUM_PROXY_TOKEN,
      anthropic: process.env.CONSORTIUM_PROXY_TOKEN_ANTHROPIC,
      openai: process.env.CONSORTIUM_PROXY_TOKEN_OPENAI,
      google: process.env.CONSORTIUM_PROXY_TOKEN_GOOGLE,
      zai: process.env.CONSORTIUM_PROXY_TOKEN_ZAI
    };
    const haveAllPaid = !!(envTokens.consortium && envTokens.anthropic && envTokens.openai && envTokens.google && envTokens.zai);
    const haveJustConsortium = !!envTokens.consortium;
    if (isFreeSlug && haveJustConsortium || haveAllPaid) {
      proxyTokens = {
        consortium: envTokens.consortium,
        ...envTokens.anthropic ? { anthropic: envTokens.anthropic } : {},
        ...envTokens.openai ? { openai: envTokens.openai } : {},
        ...envTokens.google ? { google: envTokens.google } : {},
        ...envTokens.zai ? { zai: envTokens.zai } : {}
      };
      persistence.logger.warn(`[ConsortiumCode] Proxy-token mint failed (network); using CONSORTIUM_PROXY_TOKEN_* env-var fallback.`);
    } else {
      const consortiumError = scopeErrors.consortium ?? { code: "network_error", message: failed.join("; ") };
      persistence.logger.warn(`[ConsortiumCode] Failed to mint proxy tokens for providers: ${failed.join("; ")}. Aborting (no upstream-Zen fallback).`);
      try {
        session.sendSessionEvent({
          type: "message",
          message: `Cannot start session with ${spawnModelId}: ${consortiumError.message}`,
          errorCode: consortiumError.code,
          scope: "consortium"
        });
      } catch {
      }
      const err = new Error(`mint_failed:consortium:${consortiumError.code}:${consortiumError.message}`);
      err.mintFailed = {
        scope: "consortium",
        errorCode: consortiumError.code,
        message: consortiumError.message
      };
      throw err;
    }
  } else if (isFreeSlug && !tokens.consortium) {
    const consortiumError = scopeErrors.consortium ?? { code: "mint_failed", message: failed.join("; ") };
    persistence.logger.warn(`[ConsortiumCode] Consortium-scope mint failed for free session (code=${consortiumError.code}): ${consortiumError.message}. No upstream-Zen fallback \u2014 refusing spawn.`);
    try {
      session.sendSessionEvent({
        type: "message",
        message: `Cannot start session with ${spawnModelId}: ${consortiumError.message}`,
        // Pass structured fields verbatim so the app can route to the
        // right billing/upgrade UI without re-parsing strings.
        errorCode: consortiumError.code,
        scope: "consortium"
      });
    } catch {
    }
    const err = new Error(`mint_failed:consortium:${consortiumError.code}:${consortiumError.message}`);
    err.mintFailed = {
      scope: "consortium",
      errorCode: consortiumError.code,
      message: consortiumError.message
    };
    throw err;
  } else if (!isFreeSlug && tokens.consortium) {
    proxyTokens = tokens;
    if (firstProxyBaseUrl) consortiumProxyUrl = firstProxyBaseUrl;
    persistence.logger.warn(`[ConsortiumCode] Partial proxy-token mint (${failed.join("; ")}); paid native providers without tokens are unavailable this session.`);
  } else {
    const consortiumError = scopeErrors.consortium ?? { code: "mint_failed", message: failed.join("; ") };
    persistence.logger.warn(`[ConsortiumCode] Failed to mint proxy tokens for providers: ${failed.join("; ")}`);
    try {
      session.sendSessionEvent({
        type: "message",
        message: `Cannot start session with ${spawnModelId}: ${consortiumError.message}`,
        errorCode: consortiumError.code,
        scope: "consortium"
      });
    } catch {
    }
    const err = new Error(`mint_failed:consortium:${consortiumError.code}:${consortiumError.message}`);
    err.mintFailed = {
      scope: "consortium",
      errorCode: consortiumError.code,
      message: consortiumError.message
    };
    throw err;
  }
  if (!byokActive && spawnModelId !== "opencode/big-pickle" && spawnModelId.includes("/")) {
    const [providerScope, ...rest] = spawnModelId.split("/");
    const bareModelId = rest.join("/");
    try {
      const previewRes = await axios.get(`${persistence.configuration.serverUrl}/v1/billing/wallet-preview`, {
        params: { providerId: providerScope, modelId: bareModelId },
        headers: { Authorization: `Bearer ${opts.credentials.token}` },
        timeout: 7e3,
        validateStatus: () => true
      });
      const data = previewRes.data ?? {};
      if (previewRes.status === 200 && data.ok === false) {
        const reason = data.code;
        const friendly = reason === "insufficient_credits" ? `Your workspace needs credits to use ${spawnModelId}. Required ~${data.requiredCents}\xA2, available ${data.availableCents}\xA2.` : reason === "no_managed_key" ? `${providerScope} is not currently provisioned with a managed key on this server. Contact your operator.` : `Could not resolve a billable organization for this account.`;
        persistence.logger.warn(`[ConsortiumCode] Wallet preview blocked spawn for ${spawnModelId}: ${reason}`);
        try {
          session.sendSessionEvent({
            type: "message",
            message: `Cannot start session with ${spawnModelId}: ${friendly}`
          });
        } catch {
        }
        throw new Error(`wallet_preview_blocked:${reason}:${spawnModelId}`);
      }
    } catch (err) {
      if (typeof err?.message === "string" && err.message.startsWith("wallet_preview_blocked:")) {
        throw err;
      }
      persistence.logger.debug(`[ConsortiumCode] Wallet preview check skipped: ${err?.message ?? err}`);
    }
  }
  const ensureBackend = async (model) => {
    if (backend) {
      try {
        await backend.dispose();
      } catch {
      }
      backend = null;
      acpSessionId = null;
    }
    const envModel = model || process.env.OPENCODE_MODEL;
    const envApiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
    const envBaseUrl = process.env.OPENAI_BASE_URL || process.env.ANTHROPIC_BASE_URL;
    const proxyUrl = consortiumProxyUrl;
    currentBackendModel = envModel ? envModel.includes("/") ? envModel : `opencode/${envModel}` : "opencode/big-pickle";
    const ccMcpServers = {
      consortium: {
        command: path.join(persistence.projectPath(), "bin", "consortium-mcp.mjs"),
        args: ["--url", consortiumServer.url]
      }
    };
    await index.provisionAcpSession(process.cwd(), ccMcpServers, session.sessionId);
    backend = createConsortiumCodeBackend({
      cwd: process.cwd(),
      model: envModel,
      apiKey: envApiKey,
      baseUrl: envBaseUrl,
      proxyUrl,
      proxyTokens,
      // TEMP: DEBUG-level logging so we can see what the binary's
      // consortium provider does when resolving consortium/big-pickle.
      // Without this, "log-level ERROR" hides the catalog-fetch and
      // model-resolution failures we're trying to diagnose. Drop to
      // ERROR before production.
      logLevel: "DEBUG",
      permissionHandler,
      mcpServers: ccMcpServers
    });
    backend.setScratchDir?.(scratchDir);
    backend.setAttachmentCapability?.(capabilities.resolveCapability("consortium-code", currentModel ?? null));
    backend.onMessage((msg) => {
      try {
        resetWatchdog();
        switch (msg.type) {
          case "model-output": {
            const chunk = msg.textDelta ?? msg.fullText ?? "";
            streamModelChunk(chunk);
            break;
          }
          case "status":
            resetWatchdog();
            if (msg.status === "running") {
              thinking = true;
              session.keepAlive(thinking, "remote");
            } else if (msg.status === "idle" || msg.status === "stopped") {
              if (accumulatedText.trim().length > 0) {
                finalizeTurn("backendIdle");
              } else {
                persistence.logger.debug("[ConsortiumCode] backend idle with empty accumulatedText \u2014 deferring finalize to sendPromptResolved/watchdog");
              }
            } else if (msg.status === "error") {
              const detail = typeof msg.detail === "object" ? JSON.stringify(msg.detail) : msg.detail ?? "unknown";
              session.sendAgentMessage(agent, {
                type: "message",
                message: `Error: ${detail}`,
                id: node_crypto.randomUUID()
              });
              messageBuffer.addMessage(`Error: ${detail}`, "status");
              finalizeTurn("error");
            }
            break;
          case "tool-call":
            session.sendAgentMessage(agent, {
              type: "tool-call",
              name: msg.toolName,
              callId: msg.callId,
              input: msg.args,
              id: node_crypto.randomUUID()
            });
            messageBuffer.addMessage(`Executing: ${msg.toolName}`, "tool");
            break;
          case "tool-result":
            session.sendAgentMessage(agent, {
              type: "tool-result",
              callId: msg.callId,
              output: msg.result,
              id: node_crypto.randomUUID()
            });
            break;
          case "fs-edit":
            session.sendAgentMessage(agent, {
              type: "file-edit",
              description: msg.description,
              diff: msg.diff,
              filePath: msg.path ?? "unknown",
              id: node_crypto.randomUUID()
            });
            messageBuffer.addMessage(`Edit: ${msg.path ?? "file"}`, "tool");
            break;
          case "terminal-output":
            session.sendAgentMessage(agent, {
              type: "terminal-output",
              data: msg.data,
              callId: msg.callId ?? node_crypto.randomUUID()
            });
            break;
          case "permission-request":
            session.sendAgentMessage(agent, msg);
            break;
          case "inline-media":
            (async () => {
              try {
                const m = msg;
                const bytes = Uint8Array.from(Buffer.from(m.base64, "base64"));
                const uploaded = await session.uploadArtifact({ bytes, mime: m.mime });
                session.sendAgentMessage(agent, {
                  type: "artifact",
                  artifactId: uploaded.id,
                  mime: uploaded.mime,
                  name: m.name,
                  sizeBytes: uploaded.sizeBytes,
                  id: node_crypto.randomUUID()
                });
              } catch (err) {
                persistence.logger.debug("[consortium-code] inline-media upload failed:", err);
              }
            })();
            break;
          default:
            persistence.logger.debug("[ConsortiumCode] Unhandled message type:", msg.type);
            break;
        }
      } catch (err) {
        persistence.logger.debug("[ConsortiumCode] Error forwarding message:", err);
      }
    });
    const resumeSessionId = process.env.CONSORTIUM_CODE_RESUME_SESSION_ID;
    if (resumeSessionId) {
      delete process.env.CONSORTIUM_CODE_RESUME_SESSION_ID;
    }
    try {
      let result;
      if (resumeSessionId && typeof backend.loadSession === "function") {
        persistence.logger.debug(`[ConsortiumCode] Resuming ACP session: ${resumeSessionId}`);
        result = await backend.loadSession(resumeSessionId);
      } else {
        const automationPrompt = process.env.CONSORTIUM_INITIAL_PROMPT;
        if (automationPrompt) delete process.env.CONSORTIUM_INITIAL_PROMPT;
        result = await backend.startSession(automationPrompt?.trim() ? automationPrompt : void 0);
      }
      acpSessionId = result.sessionId;
      persistence.logger.debug(`[ConsortiumCode] ACP session ${resumeSessionId ? "loaded" : "started"}: ${acpSessionId}`);
      session.updateMetadata((m) => ({ ...m, agentSessionId: acpSessionId ?? void 0 }));
    } catch (err) {
      persistence.logger.debug("[ConsortiumCode] Failed to start/load session:", err);
      throw err;
    }
    return backend;
  };
  const handleAbort = async () => {
    persistence.logger.debug("[ConsortiumCode] Abort requested");
    const discardedLocalIds = messageQueue.reset();
    session.sendAgentMessage(agent, {
      type: "turn_aborted",
      id: node_crypto.randomUUID(),
      ...discardedLocalIds.length > 0 && { discardedLocalIds }
    });
    permissionHandler.reset();
    try {
      abortController.abort();
      abortController = new AbortController();
      if (acpSessionId && backend) await backend.cancel(acpSessionId);
    } catch (err) {
      persistence.logger.debug("[ConsortiumCode] Error during abort:", err);
    }
  };
  const handleKillSession = async () => {
    persistence.logger.debug("[ConsortiumCode] Kill session requested");
    shouldExit = true;
    await handleAbort();
    try {
      session.updateMetadata((m) => ({
        ...m,
        lifecycleState: "archived",
        lifecycleStateSince: Date.now(),
        archivedBy: "cli",
        archiveReason: "User terminated"
      }));
      session.sendSessionDeath();
      await session.flush();
      await session.close();
    } catch (err) {
      persistence.logger.debug("[ConsortiumCode] Error closing session:", err);
    } finally {
      clearInterval(keepAliveInterval);
      index.stopCaffeinate();
      consortiumServer.stop();
      if (backend) try {
        await backend.dispose();
      } catch {
      }
      inkInstance?.unmount();
      if (!opts.existingSession) {
        process.exit(0);
      }
    }
  };
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
  index.registerKillSessionHandler(session.rpcHandlerManager, handleKillSession);
  if (!hasTTY) {
    console.log("  Consortium Code \u2014 waiting for prompts from the mobile app...");
    console.log("  (Press Ctrl+C to exit)");
    console.log("");
  }
  try {
    while (!shouldExit) {
      applyPendingSessionSwap();
      const batch = await messageQueue.waitForMessagesAndGetAsString(abortController.signal);
      if (!batch) {
        if (abortController.signal.aborted && !shouldExit) continue;
        break;
      }
      isProcessingMessage = true;
      messageBuffer.addMessage(batch.message.substring(0, 200), "user");
      const special = index.parseSpecialCommand(batch.message);
      if (special.type === "clear") {
        messageBuffer.addMessage("Resetting session...", "status");
        if (backend) try {
          await backend.dispose();
        } catch {
        }
        backend = null;
        acpSessionId = null;
        currentModeHash = null;
        permissionHandler.reset();
        thinking = false;
        session.keepAlive(thinking, "remote");
        session.sendSessionEvent({ type: "message", message: "Context was reset" });
        messageBuffer.addMessage("Session reset.", "status");
        isProcessingMessage = false;
        sendReady();
        continue;
      }
      if (currentModeHash && batch.hash !== currentModeHash) {
        persistence.logger.debug(`[ConsortiumCode] Mode hash changed: ${currentModeHash} \u2192 ${batch.hash}`);
        permissionHandler.setPermissionMode(batch.mode.permissionMode);
      }
      currentModeHash = batch.hash;
      thinking = true;
      session.keepAlive(thinking, "remote");
      session.sendAgentMessage(agent, { type: "task_started", id: node_crypto.randomUUID() });
      const requestedModel = batch.mode.model;
      const spawnDefaultFull = spawnModelId.includes("/") ? spawnModelId : `opencode/${spawnModelId}`;
      const requestedFull = requestedModel ? requestedModel.includes("/") ? requestedModel : `opencode/${requestedModel}` : spawnDefaultFull;
      if (backend && acpSessionId) {
        if (requestedFull && requestedFull !== currentBackendModel) {
          try {
            await backend.setSessionModel(requestedFull);
            const [scope, ...rest] = requestedFull.split("/");
            const bareModelId = rest.join("/");
            const overrideProvider = scope === "opencode" ? "consortium" : scope;
            axios.post(
              `${persistence.configuration.serverUrl}/v1/proxy/model`,
              // CC-2 / CONTRACT A: pass the ACTIVE sessionId so the override
              // is session-scoped — switching the model in this session must
              // not repaint the model pill in the user's other live sessions.
              { providerId: overrideProvider, modelId: bareModelId, sessionId: session.sessionId },
              { headers: { Authorization: `Bearer ${opts.credentials.token}` }, timeout: 5e3 }
            ).catch((err) => persistence.logger.warn(`[ConsortiumCode] in-session setModelOverride failed: ${err?.message ?? err}`));
            currentBackendModel = requestedFull;
            persistence.logger.debug(`[ConsortiumCode] Switched session model to ${requestedFull} (context preserved)`);
          } catch (err) {
            persistence.logger.warn(`[ConsortiumCode] setSessionModel failed (${err}); respawning backend`);
            if (backend) try {
              await backend.dispose();
            } catch {
            }
            backend = null;
            acpSessionId = null;
            try {
              await ensureBackend(requestedModel);
            } catch (rebuildErr) {
              persistence.logger.debug("[ConsortiumCode] Failed to rebuild backend after setSessionModel failure:", rebuildErr);
              session.sendAgentMessage(agent, {
                type: "message",
                message: `Error switching model: ${rebuildErr instanceof Error ? rebuildErr.message : String(rebuildErr)}`,
                id: node_crypto.randomUUID()
              });
              thinking = false;
              session.keepAlive(thinking, "remote");
              session.sendAgentMessage(agent, { type: "task_complete", id: node_crypto.randomUUID() });
              isProcessingMessage = false;
              sendReady();
              continue;
            }
          }
        }
      } else {
        try {
          await ensureBackend(requestedModel);
        } catch (err) {
          persistence.logger.debug("[ConsortiumCode] Failed to create backend:", err);
          session.sendAgentMessage(agent, {
            type: "message",
            message: `Error starting Consortium Code: ${err instanceof Error ? err.message : String(err)}`,
            id: node_crypto.randomUUID()
          });
          thinking = false;
          session.keepAlive(thinking, "remote");
          session.sendAgentMessage(agent, { type: "task_complete", id: node_crypto.randomUUID() });
          isProcessingMessage = false;
          sendReady();
          continue;
        }
      }
      try {
        startTurn();
        let resolved = [];
        if (index.attachmentsKillSwitchOff()) {
          pendingMessageLocalIds.length = 0;
          pendingDriveNodeRefs.length = 0;
          pendingAttachmentIds.length = 0;
          pendingRecords.length = 0;
          persistence.logger.debug("[ConsortiumCode] CONSORTIUM_ATTACHMENTS_V1 off \u2014 dropping attachments");
        } else if (attachmentResolver && driveClient) {
          const localIds = pendingMessageLocalIds.splice(0);
          const refs = pendingDriveNodeRefs.splice(0);
          const ids = pendingAttachmentIds.splice(0);
          const records = pendingRecords.splice(0);
          try {
            resolved = await index.resolvePendingAttachments(
              attachmentResolver,
              driveClient,
              session.sessionId,
              { localIds, refs, bindingOnlyIds: ids, records },
              "ConsortiumCode"
            );
          } catch (err) {
            persistence.logger.debug("[ConsortiumCode] attachment resolve failed (continuing without):", err);
            resolved = [];
          }
        } else {
          pendingMessageLocalIds.length = 0;
          pendingDriveNodeRefs.length = 0;
          pendingAttachmentIds.length = 0;
          pendingRecords.length = 0;
        }
        backend.setAttachmentCapability?.(capabilities.resolveCapability("consortium-code", currentModel ?? null));
        if (resolved.length > 0) {
          await backend.sendPrompt(acpSessionId, { text: batch.message, attachments: resolved });
        } else {
          await backend.sendPrompt(acpSessionId, batch.message);
        }
      } catch (err) {
        persistence.logger.debug("[ConsortiumCode] sendPrompt failed:", err);
        session.sendAgentMessage(agent, {
          type: "message",
          message: `Error: ${err instanceof Error ? err.message : String(err)}`,
          id: node_crypto.randomUUID()
        });
      } finally {
        sendPromptInFlight = false;
        finalizeTurn("sendPromptResolved");
        accumulatedText = "";
        isProcessingMessage = false;
        applyPendingSessionSwap();
        sendReady();
      }
    }
  } finally {
    persistence.logger.debug(`[ConsortiumCode] Main loop exited, cleaning up (switchToLocal=${switchToLocal})`);
    clearInterval(keepAliveInterval);
    clearWatchdog();
    index.stopCaffeinate();
    consortiumServer.stop();
    if (backend) try {
      await backend.dispose();
    } catch {
    }
    try {
      await scratchDir.cleanup();
    } catch {
    }
    inkInstance?.unmount();
    if (!switchToLocal) {
      try {
        session.sendSessionDeath();
        await session.flush();
        await session.close();
      } catch {
      }
    }
  }
  return switchToLocal ? "switch" : "exit";
}

exports.runConsortiumCode = runConsortiumCode;