#!/usr/bin/env node

var __defProp = Object.defineProperty;
var __export = (target, all2) => {
  for (var name in all2)
    __defProp(target, name, { get: all2[name], enumerable: true });
};

// src/service/cli.ts
import * as path22 from "node:path";

// src/service/http-server.ts
import * as http from "node:http";

// src/core/frontmatter.ts
var FENCE = "---";
var NODE_FRONTMATTER_KEY_ORDER = ["id", "type", "tags", "mode", "relations"];
function parseFrontmatter(raw) {
  const text3 = raw.replace(/\r\n/g, "\n");
  if (!text3.startsWith(FENCE + "\n")) {
    return { data: {}, body: raw, keyOrder: [] };
  }
  const end = text3.indexOf("\n" + FENCE, FENCE.length);
  if (end === -1) {
    return { data: {}, body: raw, keyOrder: [] };
  }
  const fmBlock = text3.slice(FENCE.length + 1, end);
  const afterFence = text3.indexOf("\n", end + 1);
  const body = afterFence === -1 ? "" : text3.slice(afterFence + 1);
  const data = {};
  const keyOrder = [];
  const lines = fmBlock.split("\n");
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    if (/^-\s*/.test(trimmed)) continue;
    const colon = trimmed.indexOf(":");
    if (colon === -1) continue;
    const key2 = trimmed.slice(0, colon).trim();
    let valuePart = trimmed.slice(colon + 1).trim();
    valuePart = stripInlineComment(valuePart);
    if (valuePart === "" && isBlockSequenceStart(lines[i + 1])) {
      const { value, nextIndex } = readBlockSequence(lines, i + 1, key2);
      data[key2] = normalizeValueForKey(key2, value);
      i = nextIndex - 1;
    } else {
      data[key2] = normalizeValueForKey(key2, coerceForKey(key2, valuePart));
    }
    keyOrder.push(key2);
  }
  return { data, body, keyOrder };
}
function stripInlineComment(v) {
  if (v.startsWith('"') || v.startsWith("'")) return v;
  const hash = v.indexOf(" #");
  return hash === -1 ? v : v.slice(0, hash).trim();
}
function coerce(v) {
  if (v === "") return void 0;
  if (v === "true") return true;
  if (v === "false") return false;
  if (v === "null" || v === "~") return null;
  if (/^-?\d+$/.test(v)) return parseInt(v, 10);
  if (/^-?\d*\.\d+$/.test(v)) return parseFloat(v);
  if (v.startsWith('"') && !v.endsWith('"')) {
    throw new Error("Invalid frontmatter YAML: unterminated double-quoted string.");
  }
  if (v.startsWith('"') && v.endsWith('"')) {
    return parseDoubleQuoted(v);
  }
  if (v.startsWith("'") && !v.endsWith("'")) {
    throw new Error("Invalid frontmatter YAML: unterminated single-quoted string.");
  }
  if (v.startsWith("'") && v.endsWith("'")) {
    return v.slice(1, -1).replace(/''/g, "'");
  }
  if (v.startsWith("{")) {
    if (!v.endsWith("}")) {
      throw new Error("Invalid frontmatter YAML: unterminated flow mapping.");
    }
    return parseFlowMapping(v);
  }
  if (v.startsWith("[") && !v.endsWith("]")) {
    throw new Error("Invalid frontmatter YAML: unterminated flow array.");
  }
  if (v.startsWith("[") && v.endsWith("]")) {
    const inner = v.slice(1, -1).trim();
    if (!inner) return [];
    return splitFlowCollection(inner).map((item) => coerce(item.trim()));
  }
  return v;
}
function isBlockSequenceStart(line) {
  return line !== void 0 && /^\s*-\s*/.test(line);
}
function leadingIndent(line) {
  const match = line.match(/^(\s*)/);
  return match ? match[1].length : 0;
}
function readBlockSequence(lines, startIndex, key2) {
  const value = [];
  let i = startIndex;
  while (i < lines.length) {
    const line = lines[i];
    const itemMatch = line.match(/^(\s*)-\s*(.*)$/);
    if (!itemMatch) break;
    const itemIndent = itemMatch[1].length;
    const rest = stripInlineComment(itemMatch[2].trim());
    i += 1;
    const inlineMap = rest.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/);
    if (inlineMap && !(rest.startsWith("{") || rest.startsWith("["))) {
      const obj = {};
      const firstKey = inlineMap[1];
      const firstVal = stripInlineComment(inlineMap[2].trim());
      obj[firstKey] = firstVal === "" ? void 0 : coerceForKey(key2, firstVal);
      while (i < lines.length) {
        const cont = lines[i];
        if (!cont.trim() || cont.trim().startsWith("#")) {
          i += 1;
          continue;
        }
        if (leadingIndent(cont) <= itemIndent) break;
        if (/^\s*-\s*/.test(cont)) break;
        const trimmed = cont.trim();
        const colon = trimmed.indexOf(":");
        if (colon === -1) break;
        const fieldKey = trimmed.slice(0, colon).trim();
        const fieldVal = stripInlineComment(trimmed.slice(colon + 1).trim());
        obj[fieldKey] = fieldVal === "" ? void 0 : coerceForKey(key2, fieldVal);
        i += 1;
      }
      for (const k of Object.keys(obj)) {
        if (obj[k] === void 0) delete obj[k];
      }
      value.push(obj);
      continue;
    }
    value.push(rest === "" ? null : coerceForKey(key2, rest));
  }
  return { value, nextIndex: i };
}
function parseFlowMapping(raw) {
  const inner = raw.slice(1, -1).trim();
  if (!inner) return {};
  const parts = splitFlowCollection(inner);
  const out = {};
  for (const part of parts) {
    const trimmed = part.trim();
    if (!trimmed) continue;
    const colon = findTopLevelColon(trimmed);
    if (colon === -1) {
      throw new Error(`Invalid frontmatter YAML: flow mapping entry missing colon: ${trimmed}`);
    }
    const k = trimmed.slice(0, colon).trim();
    const v = trimmed.slice(colon + 1).trim();
    if (!k) throw new Error("Invalid frontmatter YAML: empty flow mapping key.");
    out[k] = v === "" ? null : coerce(v);
  }
  return out;
}
function findTopLevelColon(s) {
  let depth = 0;
  let quote = null;
  for (let i = 0; i < s.length; i++) {
    const ch = s[i];
    if (quote) {
      if (ch === "\\" && quote === '"') {
        i += 1;
        continue;
      }
      if (ch === quote) quote = null;
      continue;
    }
    if (ch === '"' || ch === "'") {
      quote = ch;
      continue;
    }
    if (ch === "{" || ch === "[") {
      depth += 1;
      continue;
    }
    if (ch === "}" || ch === "]") {
      depth = Math.max(0, depth - 1);
      continue;
    }
    if (ch === ":" && depth === 0) return i;
  }
  return -1;
}
function splitFlowCollection(inner) {
  const items = [];
  let current = "";
  let depth = 0;
  let quote = null;
  for (let i = 0; i < inner.length; i++) {
    const ch = inner[i];
    if (quote) {
      current += ch;
      if (ch === "\\" && quote === '"' && i + 1 < inner.length) {
        current += inner[++i];
        continue;
      }
      if (ch === quote) quote = null;
      continue;
    }
    if (ch === '"' || ch === "'") {
      quote = ch;
      current += ch;
      continue;
    }
    if (ch === "{" || ch === "[") {
      depth += 1;
      current += ch;
      continue;
    }
    if (ch === "}" || ch === "]") {
      depth = Math.max(0, depth - 1);
      current += ch;
      continue;
    }
    if (ch === "," && depth === 0) {
      items.push(current);
      current = "";
      continue;
    }
    current += ch;
  }
  items.push(current);
  return items;
}
function coerceForKey(key2, raw) {
  if (key2 !== "commits") return coerce(raw);
  if (raw.startsWith("[") && raw.endsWith("]")) {
    const inner = raw.slice(1, -1).trim();
    if (!inner) return [];
    return splitFlowCollection(inner).map((item) => coerceCommitItem(item.trim()));
  }
  return coerceCommitItem(raw);
}
function coerceCommitItem(raw) {
  return /^\d+$/.test(raw) ? raw : coerce(raw);
}
function parseDoubleQuoted(v) {
  try {
    return JSON.parse(v);
  } catch {
    return unescapeYamlDoubleQuoted(v.slice(1, -1));
  }
}
function unescapeYamlDoubleQuoted(value) {
  const escapes = {
    "0": "\0",
    a: "\x07",
    b: "\b",
    t: "	",
    n: "\n",
    v: "\v",
    f: "\f",
    r: "\r",
    e: "\x1B",
    '"': '"',
    "/": "/",
    "\\": "\\"
  };
  let out = "";
  for (let i = 0; i < value.length; i++) {
    const ch = value[i];
    if (ch !== "\\" || i === value.length - 1) {
      out += ch;
      continue;
    }
    const next = value[++i];
    out += escapes[next] ?? `\\${next}`;
  }
  return out;
}
function normalizeValueForKey(key2, value) {
  if (key2 === "workspace" || key2 === "path" || key2 === "ref") {
    return normalizeWindowsPathValue(value);
  }
  if (key2 === "paths" && Array.isArray(value)) {
    return value.map((item) => normalizeWindowsPathValue(item));
  }
  return value;
}
function normalizeWindowsPathValue(value) {
  if (typeof value !== "string" || !/^[A-Za-z]:\\/.test(value)) return value;
  return value.replace(/\\{2,}/g, "\\");
}
function isPlainObject(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function serializeFrontmatter(data, body, keyOrder = []) {
  const keys = orderedKeys(data, keyOrder);
  const lines = [FENCE];
  for (const k of keys) {
    const val = data[k];
    if (val === void 0) continue;
    if (Array.isArray(val) && val.some(isPlainObject)) {
      lines.push(`${k}:`);
      if (val.length === 0) {
        lines[lines.length - 1] = `${k}: []`;
      } else {
        for (const item of val) {
          lines.push(`  - ${emit(item)}`);
        }
      }
      continue;
    }
    lines.push(`${k}: ${emit(val)}`);
  }
  lines.push(FENCE);
  const out = lines.join("\n");
  return body ? out + "\n" + body : out + "\n";
}
function orderedKeys(data, keyOrder) {
  const seen = /* @__PURE__ */ new Set();
  const result = [];
  for (const k of keyOrder) {
    if (k in data && !seen.has(k)) {
      result.push(k);
      seen.add(k);
    }
  }
  for (const k of Object.keys(data)) {
    if (!seen.has(k)) {
      result.push(k);
      seen.add(k);
    }
  }
  return result;
}
function emit(v) {
  if (typeof v === "boolean") return v ? "true" : "false";
  if (typeof v === "number") return String(v);
  if (v === null) return "null";
  if (Array.isArray(v)) {
    if (v.length === 0) return "[]";
    return "[" + v.map((item) => emit(item)).join(", ") + "]";
  }
  if (isPlainObject(v)) {
    const keys = Object.keys(v).filter((k) => v[k] !== void 0);
    if (keys.length === 0) return "{}";
    return "{" + keys.map((k) => `${k}: ${emit(v[k])}`).join(", ") + "}";
  }
  const s = String(v);
  if (/^-?(?:\d+|\d*\.\d+)$/.test(s) || /[:,#\[\]{}]/.test(s) || /[\u0000-\u001f\u007f-\u009f]/.test(s) || s !== s.trim() || s === "") {
    return JSON.stringify(s);
  }
  return s;
}

// src/core/registryRecovery.ts
async function backupCorruptRegistry(fs21, path23) {
  const backupPath = `${path23}.corrupt-${timestamp()}`;
  await fs21.writeFile(backupPath, await fs21.readFile(path23));
  return backupPath;
}
function warnRegistryRecovered(path23, backupPath, action, extra = "") {
  console.error(
    `WARNING: ${path23} was corrupt; backed up to ${backupPath} and ${action}. Review it.${extra ? ` ${extra}` : ""}`
  );
}
function timestamp() {
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
}

// src/core/paths.ts
var TENT_SYSTEM_DIR = ".tent";
var ROLES_REGISTRY_PATH = "roles.json";
var TAGS_REGISTRY_PATH = "tags.json";
var ORDER_PATH = "order.json";
var MUTATION_LOCK_PATH = "mutation.lock";
var INDEX_PATH = "index.md";
var WORKSPACE_SETTINGS_PATH = "settings.json";
var ANNOTATIONS_PATH = "annotations.json";
var TEMP_DIR = "temp";
var ATTACHMENTS_DIR = "attachments";
var ROLES_TEMP_DIR = "roles";
var SESSIONS_TEMP_DIR = "sessions";
function nodeNotePath(nodePath8) {
  const separator = nodePath8.lastIndexOf("/");
  const name = separator === -1 ? nodePath8 : nodePath8.slice(separator + 1);
  return nodePath8 === "" ? ".md" : `${nodePath8}/${name}.md`;
}
var OPERATIONAL_TOP_LEVEL = /* @__PURE__ */ new Set([
  TEMP_DIR,
  ATTACHMENTS_DIR,
  // 若仍见嵌套 .tent，视为系统区而非 Node。
  TENT_SYSTEM_DIR
]);
var SYSTEM_REGISTRY_FILES = /* @__PURE__ */ new Set([
  ROLES_REGISTRY_PATH,
  TAGS_REGISTRY_PATH,
  ORDER_PATH,
  MUTATION_LOCK_PATH,
  WORKSPACE_SETTINGS_PATH,
  ANNOTATIONS_PATH,
  INDEX_PATH,
  "log.md"
]);
function systemRootFromWorkspace(workspaceRoot) {
  const root = workspaceRoot.replace(/[\\/]+$/, "");
  const sep4 = root.includes("\\") && !root.includes("/") ? "\\" : "/";
  return `${root}${sep4}${TENT_SYSTEM_DIR}`;
}
function isOperationalPath(relativePath4) {
  const path23 = relativePath4.replace(/\\/g, "/").replace(/^\.\/+/, "");
  if (!path23) return false;
  const top = path23.split("/")[0] ?? "";
  return OPERATIONAL_TOP_LEVEL.has(top);
}
function safeOperationalSegment(value, emptyPrefix = "id") {
  const source = value.trim();
  if (!source) throw new Error("Operational segment cannot be empty.");
  const normalized = source.normalize("NFKC");
  let clean = normalized.replace(/[<>:"/\\|?*\x00-\x1f~^:[\]@{}]+/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^[.-]+|[.-]+$/g, "").slice(0, 40);
  const reserved = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(clean);
  if (reserved) clean = `${emptyPrefix}-${clean}`;
  if (!clean) {
    let h = 0;
    for (let i = 0; i < source.length; i++) h = h * 31 + source.charCodeAt(i) >>> 0;
    return `${emptyPrefix}-${h.toString(36)}`;
  }
  if (clean !== normalized || normalized !== source || reserved) {
    let h = 0;
    for (let i = 0; i < source.length; i++) h = h * 31 + source.charCodeAt(i) >>> 0;
    return `${clean}-${h.toString(36)}`;
  }
  return clean;
}
function roleTempRoot(roleId) {
  return `${TEMP_DIR}/${ROLES_TEMP_DIR}/${safeOperationalSegment(roleId, "role")}`;
}
function roleTasksDir(roleId) {
  return `${roleTempRoot(roleId)}/tasks`;
}
function sessionTempRoot(sessionId) {
  return `${TEMP_DIR}/${SESSIONS_TEMP_DIR}/${safeOperationalSegment(sessionId, "session")}`;
}
function sessionTasksDir(sessionId) {
  return `${sessionTempRoot(sessionId)}/tasks`;
}
function taskManifestPath(ownerRoot, taskId) {
  const safeTask = safeOperationalSegment(taskId, "task");
  return `${ownerRoot}/manifests/${safeTask}.yml`;
}
function isSystemNoteName(fileName) {
  return SYSTEM_REGISTRY_FILES.has(fileName) || fileName === "MIGRATED.md";
}

// src/core/order.ts
var ROOT_KEY = "__root__";
async function loadOrder(fs21) {
  if (!await fs21.exists(ORDER_PATH)) return {};
  try {
    return JSON.parse(await fs21.readFile(ORDER_PATH));
  } catch {
    const backupPath = await backupCorruptRegistry(fs21, ORDER_PATH);
    await saveOrder(fs21, {});
    warnRegistryRecovered(ORDER_PATH, backupPath, "recovered");
    return {};
  }
}
async function saveOrder(fs21, map) {
  await fs21.writeFile(ORDER_PATH, JSON.stringify(map, null, 2) + "\n");
}
function sortByOrder(items, order, fallback) {
  const sorted = [...items];
  if (!order || order.length === 0) {
    sorted.sort(fallback);
    return sorted;
  }
  const idx = new Map(order.map((id, i) => [id, i]));
  sorted.sort((a, b) => {
    const ai = idx.has(a.id) ? idx.get(a.id) : Infinity;
    const bi = idx.has(b.id) ? idx.get(b.id) : Infinity;
    if (ai !== bi) return ai - bi;
    return fallback(a, b);
  });
  return sorted;
}

// src/core/node-type.ts
function normalizeOptionalNodeType(value, label = "Node type") {
  if (value === void 0) return void 0;
  if (typeof value !== "string") {
    throw new Error(`${label} must be a string when present.`);
  }
  const type = value.trim();
  if (!type) {
    throw new Error(`${label} must be non-empty when present.`);
  }
  return type;
}

// src/core/adapter.ts
function withTentMutation(fs21, action) {
  return fs21.withLock ? fs21.withLock(MUTATION_LOCK_PATH, action) : action();
}

// src/core/relations.ts
var RELATION_ID_PREFIX = "rl-";
var REL_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
var RelationError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "RelationError";
  }
};
var CANONICAL_RELATION_KEYS = /* @__PURE__ */ new Set([
  "id",
  "kind",
  "direction",
  "label",
  "nodeId",
  "unresolved",
  "target"
]);
function isRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function makeRelationId(rand = Math.random, len = 8) {
  let s = "";
  for (let i = 0; i < len; i++) {
    s += REL_ALPHABET[Math.floor(rand() * REL_ALPHABET.length)];
  }
  return RELATION_ID_PREFIX + s;
}
function makeUniqueRelationId(existing, rand = Math.random) {
  for (let attempt = 0; attempt < 50; attempt++) {
    const id = makeRelationId(rand);
    if (!existing.has(id)) return id;
  }
  return makeRelationId(rand, 12);
}
function isRelationId(id) {
  return id.startsWith(RELATION_ID_PREFIX) && id.length > RELATION_ID_PREFIX.length;
}
function cloneRelation(record) {
  const out = {
    id: record.id,
    kind: record.kind,
    direction: record.direction,
    target: "nodeId" in record.target ? { nodeId: record.target.nodeId } : { unresolved: record.target.unresolved }
  };
  if (record.label !== void 0) out.label = record.label;
  return out;
}
function normalizeRelationTarget(raw) {
  if (!isRecord(raw)) {
    throw new RelationError("INVALID_INPUT", "relation target must be an object");
  }
  const hasNodeId = Object.prototype.hasOwnProperty.call(raw, "nodeId");
  const hasUnresolved = Object.prototype.hasOwnProperty.call(raw, "unresolved");
  if (hasNodeId && hasUnresolved) {
    throw new RelationError(
      "INVALID_INPUT",
      "relation target must be exactly one of { nodeId } or { unresolved }"
    );
  }
  if (hasNodeId) {
    if (typeof raw.nodeId !== "string" || !raw.nodeId.trim()) {
      throw new RelationError("INVALID_INPUT", "relation target.nodeId must be a non-empty string");
    }
    return { nodeId: raw.nodeId.trim() };
  }
  if (hasUnresolved) {
    if (typeof raw.unresolved !== "string" || !raw.unresolved.trim()) {
      throw new RelationError(
        "INVALID_INPUT",
        "relation target.unresolved must be a non-empty string"
      );
    }
    return { unresolved: raw.unresolved.trim() };
  }
  throw new RelationError(
    "INVALID_INPUT",
    "relation target must be exactly one of { nodeId } or { unresolved }"
  );
}
function normalizeRelationDirection(raw) {
  if (raw === "directed" || raw === "bidirectional") return raw;
  throw new RelationError(
    "INVALID_INPUT",
    'relation direction must be "directed" or "bidirectional"'
  );
}
function normalizeRelationKind(raw) {
  if (typeof raw !== "string" || !raw.trim()) {
    throw new RelationError("INVALID_INPUT", "relation kind must be a non-empty string");
  }
  const kind = raw.trim();
  if (/[\r\n]/.test(kind)) {
    throw new RelationError("INVALID_INPUT", "relation kind cannot contain newlines");
  }
  return kind;
}
function normalizeRelationLabel(raw) {
  if (raw === void 0 || raw === null) return void 0;
  if (typeof raw !== "string") {
    throw new RelationError("INVALID_INPUT", "relation label must be a string when present");
  }
  const label = raw.trim();
  return label.length > 0 ? label : void 0;
}
function parseRelationRecord(raw) {
  if (!isRecord(raw)) return null;
  if (typeof raw.id !== "string" || !isRelationId(raw.id)) return null;
  let kind;
  let direction;
  let target;
  let label;
  try {
    kind = normalizeRelationKind(raw.kind);
    direction = normalizeRelationDirection(raw.direction);
    label = normalizeRelationLabel(raw.label);
    if (isRecord(raw.target)) {
      target = normalizeRelationTarget(raw.target);
    } else if (Object.prototype.hasOwnProperty.call(raw, "nodeId") || Object.prototype.hasOwnProperty.call(raw, "unresolved")) {
      target = normalizeRelationTarget({
        ...Object.prototype.hasOwnProperty.call(raw, "nodeId") ? { nodeId: raw.nodeId } : {},
        ...Object.prototype.hasOwnProperty.call(raw, "unresolved") ? { unresolved: raw.unresolved } : {}
      });
    } else {
      return null;
    }
  } catch {
    return null;
  }
  const out = { id: raw.id, kind, direction, target };
  if (label !== void 0) out.label = label;
  return out;
}
function normalizeRelationsList(value) {
  if (!Array.isArray(value)) return [];
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of value) {
    const parsed = parseRelationRecord(item);
    if (!parsed) continue;
    if (seen.has(parsed.id)) continue;
    seen.add(parsed.id);
    out.push(parsed);
  }
  return out;
}
function assertRawRelationsCanonicalForMutation(value) {
  if (value === void 0) return [];
  if (!Array.isArray(value)) {
    throw new RelationError(
      "CORRUPT",
      "Source relations must be an array of canonical relation records"
    );
  }
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  for (let i = 0; i < value.length; i++) {
    const item = value[i];
    if (!isRecord(item)) {
      throw new RelationError(
        "CORRUPT",
        `Source relations[${i}] is not a canonical relation record`
      );
    }
    for (const key2 of Object.keys(item)) {
      if (!CANONICAL_RELATION_KEYS.has(key2)) {
        throw new RelationError(
          "CORRUPT",
          `Source relations[${i}] has unrecognized field: ${key2}`
        );
      }
    }
    if (isRecord(item.target)) {
      for (const key2 of Object.keys(item.target)) {
        if (key2 !== "nodeId" && key2 !== "unresolved") {
          throw new RelationError(
            "CORRUPT",
            `Source relations[${i}].target has unrecognized field: ${key2}`
          );
        }
      }
    }
    const parsed = parseRelationRecord(item);
    if (!parsed) {
      throw new RelationError(
        "CORRUPT",
        `Source relations[${i}] is corrupt or non-canonical`
      );
    }
    if (seen.has(parsed.id)) {
      throw new RelationError(
        "CORRUPT",
        `Source relations contain duplicate id: ${parsed.id}`
      );
    }
    seen.add(parsed.id);
    out.push(parsed);
  }
  return out;
}
async function loadCanonicalRelationsForMutation(fs21, nodePath8) {
  const notePath = nodeNotePath(nodePath8);
  const { data } = parseFrontmatter(await fs21.readFile(notePath));
  return assertRawRelationsCanonicalForMutation(data.relations);
}
function relationToFrontmatterItem(record) {
  const item = {
    id: record.id,
    kind: record.kind,
    direction: record.direction
  };
  if (record.label !== void 0) item.label = record.label;
  if ("nodeId" in record.target) item.nodeId = record.target.nodeId;
  else item.unresolved = record.target.unresolved;
  return item;
}
function relationsToFrontmatterValue(records) {
  if (records.length === 0) return void 0;
  return records.map(relationToFrontmatterItem);
}
function nodeKeyOrder(existing) {
  return [
    ...NODE_FRONTMATTER_KEY_ORDER,
    ...existing.filter((key2) => !NODE_FRONTMATTER_KEY_ORDER.includes(key2))
  ];
}
async function writeNodeRelations(fs21, node2, relations) {
  const path23 = nodeNotePath(node2.path);
  const { data, body, keyOrder } = parseFrontmatter(await fs21.readFile(path23));
  const value = relationsToFrontmatterValue(relations);
  if (value === void 0) delete data.relations;
  else data.relations = value;
  await fs21.writeFile(path23, serializeFrontmatter(data, body, nodeKeyOrder(keyOrder)));
}
function assertSourceMutable(node2) {
  if (node2.invalid) {
    throw new RelationError("INVALID", `Invalid nodes cannot own relations: ${node2.path}`);
  }
  if (node2.archived || node2.mode === "archived") {
    throw new RelationError("ARCHIVED", `Archived nodes cannot own relations: ${node2.path}`);
  }
}
function assertResolvedTargetUsable(tent, target) {
  if (!("nodeId" in target)) return;
  const targetNode = tent.byId.get(target.nodeId);
  if (!targetNode) {
    throw new RelationError("TARGET", `Relation target node not found: ${target.nodeId}`);
  }
  if (!isUsableNode(targetNode)) {
    throw new RelationError(
      "TARGET",
      `Relation target is not a usable Node: ${target.nodeId}`
    );
  }
}
function listRelationsForNode(tent, nodeId) {
  const node2 = tent.byId.get(nodeId);
  if (!node2) throw new RelationError("NOT_FOUND", `Node not found: ${nodeId}`);
  const outgoing = node2.relations.map(cloneRelation);
  const incoming = [];
  for (const other of tent.byId.values()) {
    for (const rel of other.relations) {
      if (!("nodeId" in rel.target)) continue;
      if (rel.target.nodeId !== nodeId) continue;
      if (other.id === nodeId) continue;
      incoming.push({
        ...cloneRelation(rel),
        sourceId: other.id,
        sourcePath: other.path
      });
    }
  }
  incoming.sort((a, b) => {
    const byPath = a.sourcePath.localeCompare(b.sourcePath);
    if (byPath !== 0) return byPath;
    return a.id.localeCompare(b.id);
  });
  return {
    nodeId: node2.id,
    path: node2.path,
    outgoing,
    incoming
  };
}
async function createRelation(fs21, sourceId, input, rand = Math.random, loadedTent) {
  return withTentMutation(fs21, async () => {
    const tent = loadedTent ?? await loadTent(fs21);
    const node2 = tent.byId.get(sourceId);
    if (!node2) throw new RelationError("NOT_FOUND", `Node not found: ${sourceId}`);
    assertSourceMutable(node2);
    const base = await loadCanonicalRelationsForMutation(fs21, node2.path);
    const kind = normalizeRelationKind(input.kind);
    const direction = normalizeRelationDirection(input.direction);
    const label = normalizeRelationLabel(input.label);
    const target = normalizeRelationTarget(input.target);
    assertResolvedTargetUsable(tent, target);
    const existing = /* @__PURE__ */ new Set();
    for (const b of tent.byId.values()) {
      for (const r of b.relations) existing.add(r.id);
    }
    for (const r of base) existing.add(r.id);
    const id = makeUniqueRelationId(existing, rand);
    const record = { id, kind, direction, target };
    if (label !== void 0) record.label = label;
    const next = [...base.map(cloneRelation), record];
    await writeNodeRelations(fs21, node2, next);
    node2.relations = next.map(cloneRelation);
    node2.fm.relations = relationsToFrontmatterValue(next);
    return cloneRelation(record);
  });
}
async function updateRelation(fs21, sourceId, relationId, patch, loadedTent) {
  return withTentMutation(fs21, async () => {
    const tent = loadedTent ?? await loadTent(fs21);
    const node2 = tent.byId.get(sourceId);
    if (!node2) throw new RelationError("NOT_FOUND", `Node not found: ${sourceId}`);
    assertSourceMutable(node2);
    const base = await loadCanonicalRelationsForMutation(fs21, node2.path);
    const idx = base.findIndex((r) => r.id === relationId);
    if (idx < 0) {
      throw new RelationError("NOT_FOUND", `Relation not found: ${relationId}`);
    }
    const current = cloneRelation(base[idx]);
    if (patch.kind !== void 0) current.kind = normalizeRelationKind(patch.kind);
    if (patch.direction !== void 0) {
      current.direction = normalizeRelationDirection(patch.direction);
    }
    if (Object.prototype.hasOwnProperty.call(patch, "label")) {
      const nextLabel = normalizeRelationLabel(patch.label);
      if (nextLabel === void 0) delete current.label;
      else current.label = nextLabel;
    }
    if (patch.target !== void 0) {
      current.target = normalizeRelationTarget(patch.target);
      assertResolvedTargetUsable(tent, current.target);
    }
    const next = base.map((r, i) => i === idx ? current : cloneRelation(r));
    await writeNodeRelations(fs21, node2, next);
    node2.relations = next.map(cloneRelation);
    node2.fm.relations = relationsToFrontmatterValue(next);
    return cloneRelation(current);
  });
}
async function deleteRelation(fs21, sourceId, relationId, loadedTent) {
  return withTentMutation(fs21, async () => {
    const tent = loadedTent ?? await loadTent(fs21);
    const node2 = tent.byId.get(sourceId);
    if (!node2) throw new RelationError("NOT_FOUND", `Node not found: ${sourceId}`);
    assertSourceMutable(node2);
    const base = await loadCanonicalRelationsForMutation(fs21, node2.path);
    const idx = base.findIndex((r) => r.id === relationId);
    if (idx < 0) {
      throw new RelationError("NOT_FOUND", `Relation not found: ${relationId}`);
    }
    const next = base.filter((_, i) => i !== idx).map(cloneRelation);
    await writeNodeRelations(fs21, node2, next);
    node2.relations = next.map(cloneRelation);
    const fmValue = relationsToFrontmatterValue(next);
    if (fmValue === void 0) delete node2.fm.relations;
    else node2.fm.relations = fmValue;
    return { deleted: relationId };
  });
}

// src/core/id.ts
var ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
var NODE_ID_PREFIX = "cx-";
var CONNECTION_ID_RE = /^[a-z][a-z0-9-]{0,62}$/;
function isConnectionId(value) {
  return typeof value === "string" && CONNECTION_ID_RE.test(value);
}
var ROLE_ID_PREFIX = "rl-";
function deterministicDigest(input, byteLen = 32) {
  const out = new Uint8Array(byteLen);
  for (let offset = 0; offset < byteLen; offset += 4) {
    let h = (2166136261 ^ Math.imul(offset + 1, 2654435769)) >>> 0;
    const salted = `${offset}\0${input}`;
    for (let i = 0; i < salted.length; i++) {
      h ^= salted.charCodeAt(i);
      h = Math.imul(h, 16777619) >>> 0;
    }
    h ^= h >>> 16;
    h = Math.imul(h, 2246822507) >>> 0;
    h ^= h >>> 13;
    h = Math.imul(h, 3266489909) >>> 0;
    h ^= h >>> 16;
    out[offset] = h & 255;
    if (offset + 1 < byteLen) out[offset + 1] = h >>> 8 & 255;
    if (offset + 2 < byteLen) out[offset + 2] = h >>> 16 & 255;
    if (offset + 3 < byteLen) out[offset + 3] = h >>> 24 & 255;
  }
  return out;
}
function encodeAlphabetBytes(bytes, len) {
  let s = "";
  for (let i = 0; i < len; i++) {
    const b = bytes[i % bytes.length] ^ i * 17 & 255;
    s += ALPHABET[b % ALPHABET.length];
  }
  return s;
}
function makePrefixedId(prefix, rand = Math.random, len = 6) {
  let s = "";
  for (let i = 0; i < len; i++) {
    s += ALPHABET[Math.floor(rand() * ALPHABET.length)];
  }
  return prefix + s;
}
function makeUniquePrefixedId(prefix, existing, rand = Math.random) {
  for (let attempt = 0; attempt < 50; attempt++) {
    const id = makePrefixedId(prefix, rand);
    if (!existing.has(id)) return id;
  }
  return makePrefixedId(prefix, rand, 10);
}
function makeNodeId(rand = Math.random, len = 6) {
  return makePrefixedId(NODE_ID_PREFIX, rand, len);
}
function makeUniqueNodeId(existing, rand = Math.random) {
  return makeUniquePrefixedId(NODE_ID_PREFIX, existing, rand);
}
function makeUniqueRoleId(existing, rand = Math.random) {
  return makeUniquePrefixedId(ROLE_ID_PREFIX, existing, rand);
}
function deterministicRoleIdFromName(name, existing = /* @__PURE__ */ new Set()) {
  const key2 = name.trim();
  const digest = deterministicDigest(`tent.role.id.v1:${key2}`, 32);
  for (let len = 6; len <= 16; len++) {
    const id = ROLE_ID_PREFIX + encodeAlphabetBytes(digest, len);
    if (!existing.has(id)) return id;
  }
  const fallback = deterministicDigest(
    `tent.role.id.v1.fallback:${key2}:${[...existing].sort().join(",")}`,
    32
  );
  return ROLE_ID_PREFIX + encodeAlphabetBytes(fallback, 12);
}
function isNodeId(id) {
  return /^cx-[a-z0-9]+$/i.test(id);
}
function isRoleId(id) {
  return /^rl-[a-z0-9]+$/i.test(id);
}
function isSessionId(id) {
  return /^ss-[a-z0-9]+$/i.test(id);
}

// src/core/etag.ts
import { createHash } from "node:crypto";
function contentEtag(content3) {
  return createHash("sha256").update(content3, "utf8").digest("hex").slice(0, 24);
}

// src/core/tree.ts
async function loadTent(fs21) {
  const byId = /* @__PURE__ */ new Map();
  const byPath = /* @__PURE__ */ new Map();
  const roots = [];
  const top = await fs21.listDir("");
  for (const entry of top) {
    if (!entry.isDir) continue;
    if (OPERATIONAL_TOP_LEVEL.has(entry.name)) continue;
    if (isSystemNoteName(entry.name)) continue;
    await loadNodeInto(fs21, entry.name, null, roots);
  }
  const order = await loadOrder(fs21);
  const sortedRoots = sortByOrder(roots, order[ROOT_KEY], (a, b) => a.name.localeCompare(b.name));
  for (const root of sortedRoots) sortChildren(root, order);
  for (const root of sortedRoots) resolveSubtree(root);
  const duplicateIds = findDuplicateIds(sortedRoots);
  for (const root of sortedRoots) applyDuplicateInvalid(root, duplicateIds);
  for (const root of sortedRoots) indexSubtree(root, byId, byPath, duplicateIds);
  return { roots: sortedRoots, byId, byPath, duplicateIds };
}
function findDuplicateIds(roots) {
  const counts = /* @__PURE__ */ new Map();
  const visit = (node2) => {
    if (node2.id) counts.set(node2.id, (counts.get(node2.id) || 0) + 1);
    for (const child of node2.children) visit(child);
  };
  for (const root of roots) visit(root);
  return new Set([...counts].filter(([, count]) => count > 1).map(([id]) => id));
}
function applyDuplicateInvalid(node2, duplicateIds, inherited) {
  const direct = duplicateIds.has(node2.id) ? { rootId: node2.id, reason: `Duplicate id: ${node2.id}; native copies must be converted to forks.` } : void 0;
  const invalid = inherited || direct;
  if (invalid) {
    node2.invalid = true;
    node2.invalidRootId = invalid.rootId;
    node2.invalidReason = invalid.reason;
  }
  for (const child of node2.children) applyDuplicateInvalid(child, duplicateIds, invalid);
}
function sortChildren(node2, order) {
  node2.children = sortByOrder(node2.children, order[node2.id], (a, b) => a.name.localeCompare(b.name));
  for (const c of node2.children) sortChildren(c, order);
}
async function loadNode(fs21, path23, parent) {
  if (isOperationalPath(path23)) return null;
  const nodeFile = nodeNotePath(path23);
  if (!await fs21.exists(nodeFile)) {
    return null;
  }
  const raw = await fs21.readFile(nodeFile);
  let parsed;
  let parseError;
  try {
    parsed = parseFrontmatter(raw);
  } catch (error) {
    parseError = error instanceof Error ? error.message : String(error);
    parsed = { data: {}, body: raw, keyOrder: [] };
  }
  const { data, body } = parsed;
  const name = baseName(path23);
  const schemaError = canonicalIdentityError(data);
  const { fm, tags, relations } = normalizeIdentity(data);
  const node2 = {
    id: fm.id,
    type: fm.type,
    tags,
    relations,
    mode: "editable",
    archived: false,
    invalid: !!parseError || !!schemaError,
    path: path23,
    name,
    fm,
    etag: contentEtag(raw),
    body,
    children: [],
    parent
  };
  if (parseError || schemaError) {
    node2.invalidRootId = path23;
    node2.invalidReason = parseError ? `Invalid frontmatter: ${parseError}` : schemaError;
  }
  const sub = await fs21.listDir(path23);
  for (const entry of sub) {
    if (!entry.isDir) continue;
    if (OPERATIONAL_TOP_LEVEL.has(entry.name)) continue;
    await loadNodeInto(fs21, join(path23, entry.name), node2, node2.children);
  }
  return node2;
}
function canonicalIdentityError(data) {
  if (typeof data.id !== "string" || !isNodeId(data.id)) {
    return `Invalid Node id: ${typeof data.id === "string" && data.id ? data.id : "<missing>"}; canonical Node ids must start with cx-.`;
  }
  if (data.mode !== void 0 && parseNodeMode(data.mode) === void 0) {
    return `Invalid Node mode: ${String(data.mode)}.`;
  }
  try {
    normalizeOptionalNodeType(data.type);
  } catch (error) {
    return error instanceof Error ? error.message : String(error);
  }
  return void 0;
}
function normalizeIdentity(data) {
  let type;
  try {
    type = normalizeOptionalNodeType(data.type);
  } catch {
    type = void 0;
  }
  const fm = {
    ...data,
    id: typeof data.id === "string" ? data.id : "",
    ...type ? { type } : {}
  };
  if (!type) delete fm.type;
  const tags = normalizeTags(data.tags);
  if (tags.length > 0) fm.tags = tags;
  else delete fm.tags;
  const mode = parseNodeMode(data.mode);
  if (mode && mode !== "editable") fm.mode = mode;
  else delete fm.mode;
  const relations = normalizeRelationsList(data.relations);
  const fmRelations = relationsToFrontmatterValue(relations);
  if (fmRelations) fm.relations = fmRelations;
  else delete fm.relations;
  return { fm, tags, relations };
}
function parseNodeMode(value) {
  if (value === "archived") return "archived";
  if (value === "editable") return "editable";
  return void 0;
}
function isExplicitArchiveRoot(node2) {
  return node2.fm.mode === "archived";
}
function normalizeTags(value) {
  if (!Array.isArray(value)) return [];
  const out = [];
  for (const item of value) {
    if (typeof item !== "string") continue;
    const tag = item.trim();
    if (tag && !out.includes(tag)) out.push(tag);
  }
  return out;
}
async function loadNodeInto(fs21, path23, parent, target) {
  if (isOperationalPath(path23)) return;
  const node2 = await loadNode(fs21, path23, parent);
  if (node2) {
    target.push(node2);
    return;
  }
  const sub = await fs21.listDir(path23);
  for (const entry of sub) {
    if (!entry.isDir) continue;
    if (OPERATIONAL_TOP_LEVEL.has(entry.name)) continue;
    await loadNodeInto(fs21, join(path23, entry.name), parent, target);
  }
}
function resolveSubtree(node2, inheritedInvalid, inheritedArchived = false) {
  const directInvalid = node2.invalid ? { rootId: node2.invalidRootId || node2.path, reason: node2.invalidReason || "Invalid frontmatter." } : invalidIdentityReference(node2);
  const invalid = inheritedInvalid || directInvalid;
  node2.invalid = !!invalid;
  node2.invalidRootId = invalid?.rootId;
  node2.invalidReason = invalid?.reason;
  const localMode = parseNodeMode(node2.fm.mode) ?? "editable";
  node2.archived = inheritedArchived || localMode === "archived";
  node2.mode = node2.archived ? "archived" : "editable";
  if (localMode === "archived" && !inheritedArchived) node2.fm.mode = "archived";
  else delete node2.fm.mode;
  for (const c of node2.children) resolveSubtree(c, invalid, node2.archived);
}
function invalidIdentityReference(node2) {
  if (!isNodeId(node2.id)) {
    return {
      rootId: node2.path,
      reason: `Invalid Node id: ${node2.id || "<missing>"}; canonical Node ids must start with cx-.`
    };
  }
  if (node2.fm.mode !== void 0 && parseNodeMode(node2.fm.mode) === void 0) {
    return { rootId: node2.id, reason: `Invalid Node mode: ${String(node2.fm.mode)}.` };
  }
  return void 0;
}
function isUsableNode(node2) {
  return !node2.invalid && !node2.archived;
}
function assertContentMutable(node2, action = "modified") {
  if (node2.invalid) throw new Error(`Invalid nodes cannot be ${action}.`);
  if (node2.archived || node2.mode === "archived") {
    throw new Error(`Archived nodes cannot be ${action}.`);
  }
}
function isContentMutable(node2) {
  return !node2.invalid && node2.mode === "editable" && !node2.archived;
}
function indexSubtree(node2, byId, byPath, duplicateIds) {
  if (!node2.invalid && isNodeId(node2.id) && !duplicateIds.has(node2.id)) byId.set(node2.id, node2);
  byPath.set(node2.path, node2);
  for (const c of node2.children) indexSubtree(c, byId, byPath, duplicateIds);
}
function join(...parts) {
  return parts.filter((p) => p !== "").join("/");
}
function baseName(path23) {
  const i = path23.lastIndexOf("/");
  return i === -1 ? path23 : path23.slice(i + 1);
}
function dirName(path23) {
  const i = path23.lastIndexOf("/");
  return i === -1 ? "" : path23.slice(0, i);
}

// src/core/manifest.ts
function buildManifest(input) {
  const roleId = input.roleId?.trim();
  const sessionId = input.sessionId?.trim();
  if (!roleId && !sessionId) throw new Error("Manifest requires roleId or sessionId.");
  const selectedNodes = requireSelectedNodes(input);
  const selectedScope = selectedNodes.flatMap(subtree);
  const readable = [];
  const writable = [];
  for (const node2 of selectedScope) {
    if (isUsableNode(node2)) {
      readable.push({ id: node2.id, path: node2.path, note: oneLineNote(node2) });
    }
  }
  readable.push({ path: "roles.json", note: "System registry: available roles and persistent prompts." });
  readable.push({ path: "temp/", note: "System pipeline: read all role temp state." });
  for (const node2 of selectedScope) {
    if (isUsableNode(node2)) {
      writable.push({ id: node2.id, path: node2.path });
    }
  }
  for (const node2 of selectedScope) {
    if (isUsableNode(node2)) {
      writable.push({ id: node2.id, path: `${node2.path}/`, note: "Structural permission: may create/move/delete child nodes under this node." });
    }
  }
  const executorRoot = roleId ? join("temp", "roles", roleId) : join("temp", "sessions", sessionId);
  writable.push({ path: executorRoot + "/" });
  return {
    tent: input.tentName,
    ...roleId ? { roleId } : {},
    ...sessionId ? { sessionId } : {},
    // No claims[] — writable ids/paths encode selection; Task Node refs are contextCard only.
    ...input.workspace ? { workspace: input.workspace } : {},
    ...input.worktree ? { worktree: input.worktree } : {},
    ...input.branch ? { branch: input.branch } : {},
    ...input.targetBranch ? { targetBranch: input.targetBranch } : {},
    readable: dedupe(readable),
    writable: dedupe(writable)
  };
}
function manifestToYaml(m) {
  const lines = [];
  lines.push(`tent: ${m.tent}`);
  if (m.roleId) lines.push(`roleId: ${m.roleId}`);
  if (m.sessionId) lines.push(`sessionId: ${m.sessionId}`);
  if (m.workspace) lines.push(`workspace: ${yamlStr(m.workspace)}`);
  if (m.worktree) lines.push(`worktree: ${yamlStr(m.worktree)}`);
  if (m.branch) lines.push(`branch: ${yamlStr(m.branch)}`);
  if (m.targetBranch) lines.push(`targetBranch: ${yamlStr(m.targetBranch)}`);
  lines.push(`readable:`);
  for (const e of m.readable) lines.push(`  - ${entryLine(e)}`);
  lines.push(`writable:`);
  for (const e of m.writable) lines.push(`  - ${entryLine(e)}`);
  return lines.join("\n") + "\n";
}
function entryLine(e) {
  const parts = [];
  if (e.id) parts.push(`id: ${e.id}`);
  parts.push(`path: ${yamlStr(e.path)}`);
  if (e.note) parts.push(`note: ${yamlStr(e.note)}`);
  return `{${parts.join(", ")}}`;
}
function yamlStr(s) {
  return /[:#{}\[\],]/.test(s) ? JSON.stringify(s) : s;
}
function oneLineNote(node2) {
  const firstLine = node2.body.split("\n").map((l) => l.trim()).find((l) => l && !l.startsWith("#"));
  return firstLine ? firstLine.slice(0, 40) : node2.type ?? "";
}
function subtree(node2) {
  const out = [node2];
  for (const c of node2.children) out.push(...subtree(c));
  return out;
}
function requireSelectedNodes(input) {
  if (!Array.isArray(input.selectedNodes)) throw new Error("Missing selected nodes.");
  return input.selectedNodes;
}
function dedupe(entries) {
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const e of entries) {
    const key2 = `${e.id ?? ""}|${e.path}`;
    if (seen.has(key2)) continue;
    seen.add(key2);
    out.push(e);
  }
  return out;
}

// src/core/tags.ts
async function loadTagRegistry(fs21) {
  if (!await fs21.exists(TAGS_REGISTRY_PATH)) return { tags: [] };
  try {
    return normalizeRegistry(JSON.parse(await fs21.readFile(TAGS_REGISTRY_PATH)));
  } catch {
    const backupPath = await backupCorruptRegistry(fs21, TAGS_REGISTRY_PATH);
    const recovered = await recoverTagRegistryFromNodes(fs21);
    await saveTagRegistryUnlocked(fs21, recovered);
    warnRegistryRecovered(TAGS_REGISTRY_PATH, backupPath, "recovered");
    return recovered;
  }
}
async function saveTagRegistryUnlocked(fs21, registry) {
  await fs21.writeFile(TAGS_REGISTRY_PATH, JSON.stringify(normalizeRegistry(registry), null, 2) + "\n");
}
async function addRegistryTag(fs21, name) {
  await withTentMutation(fs21, async () => addRegistryTagUnlocked(fs21, name));
}
async function addRegistryTagUnlocked(fs21, name) {
  const tag = normalizeTagName(name);
  const registry = await loadTagRegistry(fs21);
  if (!registry.tags.includes(tag)) {
    registry.tags.push(tag);
    await saveTagRegistryUnlocked(fs21, registry);
  }
}
async function addTag(fs21, nodeId, name) {
  await withTentMutation(fs21, async () => {
    const tag = normalizeTagName(name);
    const tent = await loadTent(fs21);
    if (tent.duplicateIds.has(nodeId)) throw new Error(`Duplicate node id '${nodeId}' found; repair or fork the duplicate nodes before using this id.`);
    const node2 = tent.byId.get(nodeId);
    if (!node2) throw new Error(`Node not found: ${nodeId}.`);
    if (!isUsableNode(node2)) throw new Error("Invalid or archived nodes cannot be tagged.");
    assertContentMutable(node2, "tagged");
    await addRegistryTagUnlocked(fs21, tag);
    const tags = uniqueSorted([...node2.tags, tag]);
    await writeNodeTags(fs21, node2, tags);
  });
}
async function removeTag(fs21, nodeId, name) {
  await withTentMutation(fs21, async () => {
    const tag = normalizeTagName(name);
    const tent = await loadTent(fs21);
    if (tent.duplicateIds.has(nodeId)) throw new Error(`Duplicate node id '${nodeId}' found; repair or fork the duplicate nodes before using this id.`);
    const node2 = tent.byId.get(nodeId);
    if (!node2) throw new Error(`Node not found: ${nodeId}.`);
    if (!isUsableNode(node2)) throw new Error("Invalid or archived nodes cannot be tagged.");
    assertContentMutable(node2, "tagged");
    await writeNodeTags(fs21, node2, node2.tags.filter((item) => item !== tag));
  });
}
async function removeRegistryTag(fs21, name) {
  await withTentMutation(fs21, async () => {
    const tag = normalizeTagName(name);
    const registry = await loadTagRegistry(fs21);
    await saveTagRegistryUnlocked(fs21, { tags: registry.tags.filter((item) => item !== tag) });
    const tent = await loadTent(fs21);
    for (const node2 of tent.byId.values()) {
      if (node2.tags.includes(tag)) {
        await writeNodeTags(fs21, node2, node2.tags.filter((item) => item !== tag));
      }
    }
  });
}
async function syncTagRegistryAfterNodeTagsChange(fs21, previousTags, nextTags) {
  await withTentMutation(fs21, async () => {
    await syncTagRegistryAfterNodeTagsChangeUnlocked(fs21, previousTags, nextTags);
  });
}
async function syncTagRegistryAfterNodeTagsChangeUnlocked(fs21, previousTags, nextTags) {
  const previous2 = new Set(normalizeTagList(previousTags));
  const next = normalizeTagList(nextTags);
  const added = next.filter((tag) => !previous2.has(tag));
  for (const tag of added) {
    await addRegistryTagUnlocked(fs21, tag);
  }
}
function normalizeTagName(name) {
  const tag = name.trim();
  if (!tag) throw new Error("Tag name cannot be empty.");
  if (/[\/\\\r\n]/.test(tag)) throw new Error("Tag name cannot contain path separators or newlines.");
  return tag;
}
async function writeNodeTags(fs21, node2, tags) {
  const path23 = nodeNotePath(node2.path);
  const { data, body, keyOrder } = parseFrontmatter(await fs21.readFile(path23));
  const next = uniqueSorted(tags);
  if (next.length === 0) delete data.tags;
  else data.tags = next;
  await fs21.writeFile(path23, serializeFrontmatter(data, body, nodeKeyOrder2(keyOrder)));
}
function normalizeRegistry(value) {
  if (!isRecord2(value) || !Array.isArray(value.tags)) return { tags: [] };
  const tags = [];
  for (const valueTag of value.tags) {
    if (typeof valueTag !== "string") continue;
    try {
      tags.push(normalizeTagName(valueTag));
    } catch {
    }
  }
  return { tags: uniqueSorted(tags) };
}
async function recoverTagRegistryFromNodes(fs21) {
  const tent = await loadTent(fs21);
  const tags = [];
  for (const node2 of tent.byPath.values()) {
    tags.push(...node2.tags);
  }
  return { tags: uniqueSorted(tags) };
}
function normalizeTagList(values) {
  const tags = [];
  for (const value of values) {
    if (typeof value !== "string") continue;
    try {
      const tag = normalizeTagName(value);
      if (!tags.includes(tag)) tags.push(tag);
    } catch {
    }
  }
  return uniqueSorted(tags);
}
function uniqueSorted(values) {
  return [...new Set(values)].sort((a, b) => a.localeCompare(b));
}
function nodeKeyOrder2(existing) {
  return [
    ...NODE_FRONTMATTER_KEY_ORDER,
    ...existing.filter((key2) => !NODE_FRONTMATTER_KEY_ORDER.includes(key2))
  ];
}
function isRecord2(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

// src/core/skillRoleRegistry.ts
var DEFAULT_ROLES_REGISTRY = {
  roles: []
};
async function loadRolesRegistry(fs21) {
  const { registry } = await readRolesRegistryState(fs21);
  return registry;
}
async function loadRolesRegistryForMutation(fs21) {
  return loadRolesRegistry(fs21);
}
async function readRolesRegistryState(fs21) {
  if (!await fs21.exists(ROLES_REGISTRY_PATH)) {
    return {
      registry: cloneDefaultRoles(),
      recovered: false
    };
  }
  try {
    const rawText = await fs21.readFile(ROLES_REGISTRY_PATH);
    const parsed = JSON.parse(rawText);
    const registry = normalizeRolesRegistry(parsed);
    return { registry, recovered: false };
  } catch {
    const backupPath = await backupCorruptRegistry(fs21, ROLES_REGISTRY_PATH);
    const reset = cloneDefaultRoles();
    await writeJson(fs21, ROLES_REGISTRY_PATH, serializeRolesRegistry(reset));
    warnRegistryRecovered(
      ROLES_REGISTRY_PATH,
      backupPath,
      "reset",
      "IMPORTANT: role definitions cannot be inferred; restore needed roles from the backup."
    );
    return {
      registry: reset,
      recovered: true
    };
  }
}
async function createRole(fs21, definition2, rand = Math.random) {
  await withTentMutation(fs21, async () => {
    const registry = await loadRolesRegistryForMutation(fs21);
    const usedIds = roleIdSet(registry.roles);
    const role = normalizeRoleDefinition(definition2, {
      usedIds,
      assignMissingId: "random",
      rand
    });
    if (!role.name) throw new Error("Role name cannot be empty.");
    assertRoleNameAvailable(role.name);
    if (registry.roles.some((item) => item.name === role.name)) {
      throw new Error(`Role already exists: ${role.name}.`);
    }
    if (registry.roles.some((item) => item.id === role.id)) {
      throw new Error(`Role id already exists: ${role.id}.`);
    }
    registry.roles.push(role);
    await writeJson(fs21, ROLES_REGISTRY_PATH, serializeRolesRegistry(registry));
  });
}
function assertRoleNameAvailable(name) {
  if ([ROLES_TEMP_DIR, SESSIONS_TEMP_DIR].includes(name.trim().toLowerCase())) {
    throw new Error(`Role name is reserved by Tent: ${name}.`);
  }
}
async function updateRole(fs21, ref, patch) {
  await withTentMutation(fs21, async () => {
    const registry = await loadRolesRegistryForMutation(fs21);
    const index2 = findRoleIndex(registry.roles, ref);
    if (index2 === -1) throw new Error(`Role does not exist: ${ref}.`);
    const current = registry.roles[index2];
    if (patch.id !== void 0 && patch.id !== current.id) {
      throw new Error("Role id is immutable.");
    }
    if (patch.name !== void 0 && patch.name.trim() !== current.name) {
      throw new Error(
        "Role operational name cannot be renamed in this batch (temp/path migration is deferred); change displayName instead."
      );
    }
    const next = normalizeRoleDefinition(
      {
        ...current,
        ...patch,
        id: current.id,
        name: current.name
      },
      { usedIds: roleIdSet(registry.roles, current.id), assignMissingId: "keep" }
    );
    if (Object.prototype.hasOwnProperty.call(patch, "displayName")) {
      const dn = typeof patch.displayName === "string" ? patch.displayName.trim() : "";
      next.displayName = dn || current.name;
    }
    registry.roles[index2] = next;
    await writeJson(fs21, ROLES_REGISTRY_PATH, serializeRolesRegistry(registry));
  });
}
async function deleteRole(fs21, ref, confirmation) {
  await withTentMutation(fs21, async () => {
    const registry = await loadRolesRegistryForMutation(fs21);
    const index2 = findRoleIndex(registry.roles, ref);
    if (index2 === -1) throw new Error(`Role does not exist: ${ref}.`);
    const role = registry.roles[index2];
    if (confirmation !== role.name && confirmation !== role.id) {
      throw new Error(
        `Confirmation mismatch; enter the role name ${role.name} or id ${role.id}.`
      );
    }
    registry.roles.splice(index2, 1);
    await writeJson(fs21, ROLES_REGISTRY_PATH, serializeRolesRegistry({ roles: registry.roles }));
  });
}
function resolveRole(roles, ref) {
  const key2 = typeof ref === "string" ? ref.trim() : "";
  if (!key2) return void 0;
  const byId = roles.find((role) => role.id === key2);
  if (byId) return byId;
  return roles.find((role) => role.name === key2);
}
function findRoleIndex(roles, ref) {
  const key2 = typeof ref === "string" ? ref.trim() : "";
  if (!key2) return -1;
  let idx = roles.findIndex((role) => role.id === key2);
  if (idx !== -1) return idx;
  return roles.findIndex((role) => role.name === key2);
}
function normalizeRolesRegistry(value) {
  const root = isRecord3(value) ? value : {};
  const roles = [];
  const usedIds = /* @__PURE__ */ new Set();
  if (Array.isArray(root.roles)) {
    for (const item of root.roles) {
      if (!isRecord3(item)) continue;
      const role = normalizeRoleDefinition(item, {
        usedIds,
        assignMissingId: "deterministic"
      });
      if (!role.name || roles.some((existing) => existing.name === role.name)) continue;
      if (roles.some((existing) => existing.id === role.id)) continue;
      usedIds.add(role.id);
      roles.push(role);
    }
  }
  return { roles };
}
function normalizeRoleDefinition(value, opts = {}) {
  const name = typeof value.name === "string" ? value.name.trim() : "";
  const usedIds = opts.usedIds ?? /* @__PURE__ */ new Set();
  const assign = opts.assignMissingId ?? "deterministic";
  let id = typeof value.id === "string" ? value.id.trim() : "";
  if (id && !isRoleId(id)) {
    id = "";
  }
  if (id && usedIds.has(id) && assign !== "keep") {
    id = "";
  }
  if (!id) {
    if (assign === "random") {
      id = makeUniqueRoleId(usedIds, opts.rand ?? Math.random);
    } else if (name) {
      id = deterministicRoleIdFromName(name, usedIds);
    } else {
      id = makeUniqueRoleId(usedIds, opts.rand ?? Math.random);
    }
  }
  const displayRaw = typeof value.displayName === "string" ? value.displayName.trim() : "";
  const displayName = displayRaw || name;
  const role = { id, name, displayName };
  if (typeof value.prompt === "string" && value.prompt.trim()) role.prompt = value.prompt.trim();
  if (typeof value.description === "string" && value.description.trim()) {
    role.description = value.description.trim();
  }
  if (typeof value.color === "string" && value.color.trim()) role.color = value.color.trim();
  return role;
}
function roleIdSet(roles, exceptId) {
  const set = /* @__PURE__ */ new Set();
  for (const role of roles) {
    if (!role.id) continue;
    if (exceptId && role.id === exceptId) continue;
    set.add(role.id);
  }
  return set;
}
function serializeRolesRegistry(registry) {
  return {
    roles: registry.roles.map((role) => {
      const row = {
        id: role.id,
        name: role.name,
        displayName: role.displayName || role.name
      };
      if (role.prompt) row.prompt = role.prompt;
      if (role.description) row.description = role.description;
      if (role.color) row.color = role.color;
      return row;
    })
  };
}
function cloneDefaultRoles() {
  return {
    roles: DEFAULT_ROLES_REGISTRY.roles.map((role) => ({ ...role }))
  };
}
async function writeJson(fs21, path23, value) {
  if (!await fs21.exists(".tent")) await fs21.mkdir(".tent");
  await fs21.writeFile(path23, JSON.stringify(value, null, 2) + "\n");
}
function isRecord3(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

// src/core/task.ts
import { Buffer as Buffer2 } from "node:buffer";

// src/core/task-model.ts
var DEFAULT_ACCEPT_MODE = "review-required";
var TaskLifecycleError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "TaskLifecycleError";
  }
};
function isTaskActorKind(value) {
  return value === "user" || value === "role";
}
function parseTaskActorRef(value, label) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TaskLifecycleError(
      "INVALID_ACTOR",
      `Task ${label} must be an object { kind, id }.`
    );
  }
  const raw = value;
  const kind = raw.kind;
  const id = typeof raw.id === "string" ? raw.id.trim() : "";
  if (!isTaskActorKind(kind)) {
    throw new TaskLifecycleError(
      "INVALID_ACTOR",
      `Task ${label}.kind must be user|role; got ${String(kind)}.`
    );
  }
  if (!id) {
    throw new TaskLifecycleError(
      "INVALID_ACTOR",
      `Task ${label}.id must be a non-empty string.`
    );
  }
  if (kind === "user" && id !== "user") {
    throw new TaskLifecycleError(
      "INVALID_ACTOR",
      `Task ${label} with kind=user requires id "user"; got ${id}.`
    );
  }
  if (kind === "role" && id === "user") {
    throw new TaskLifecycleError(
      "INVALID_ACTOR",
      `Task ${label} with kind=role must name a durable role (not user).`
    );
  }
  return { kind, id };
}
function allowsNonReviewAcceptMode(input) {
  const parent = input.requester;
  return Boolean(parent && parent.kind === "user" && parent.id === "user");
}
function parseTaskOutcomeReport(text3) {
  const raw = typeof text3 === "string" ? text3.replace(/^\uFEFF/, "") : "";
  if (!raw.trim()) return null;
  const lines = raw.replace(/\r\n/g, "\n").split("\n");
  let i = 0;
  while (i < lines.length && lines[i].trim() === "") i += 1;
  if (i >= lines.length) return null;
  let fence = false;
  if (lines[i].trim() === "---") {
    fence = true;
    i += 1;
    while (i < lines.length && lines[i].trim() === "") i += 1;
  }
  if (i >= lines.length) return null;
  const match = lines[i].trim().match(/^outcome\s*:\s*(blocked)\s*$/i);
  if (!match) return null;
  const outcome = match[1].toLowerCase();
  i += 1;
  if (fence) {
    while (i < lines.length && lines[i].trim() === "") i += 1;
    if (i < lines.length && lines[i].trim() === "---") i += 1;
  }
  if (i < lines.length && lines[i].trim() === "") i += 1;
  const report = lines.slice(i).join("\n").replace(/^\n+/, "").replace(/\n+$/, "");
  return { outcome, report };
}
function isAcceptMode(value) {
  return value === "review-required" || value === "auto-accept" || value === "agent-decide";
}
var ACTIVE_TASK_STATES = /* @__PURE__ */ new Set([
  "queued",
  "running",
  "waiting",
  "submitted"
]);
var TERMINAL_TASK_STATES = /* @__PURE__ */ new Set([
  "accepted",
  "rejected",
  "interrupted",
  "failed"
]);
function isActiveTaskState(state) {
  return ACTIVE_TASK_STATES.has(state);
}
function makeTaskId(rand = Math.random, len = 8) {
  const stem = makeNodeId(rand, len).slice(3);
  return `tk-${stem}`;
}
function makeTaskResultId(rand = Math.random, len = 8) {
  const stem = makeNodeId(rand, len).slice(3);
  return `rs-${stem}`;
}
function isTaskId(id) {
  return /^tk-[a-z0-9]+$/.test(id);
}
function isTaskResultId(id) {
  return /^rs-[a-z0-9]+$/.test(id);
}
function assertTransition(from, event, to) {
  const ok2 = allowedTransitions(from).some((t) => t.event === event && t.to === to);
  if (!ok2) {
    throw new TaskLifecycleError(
      "INVALID_TRANSITION",
      `Invalid task transition: ${from} --${event}\u2192 ${to}`
    );
  }
}
function allowedTransitions(from) {
  switch (from) {
    case "queued":
      return [
        { event: "claim", to: "running" },
        { event: "cancel", to: "interrupted" },
        { event: "interrupt", to: "interrupted" }
      ];
    case "running":
      return [
        { event: "wait", to: "waiting" },
        { event: "submit", to: "submitted" },
        { event: "interrupt", to: "interrupted" },
        { event: "fail", to: "failed" }
      ];
    case "waiting":
      return [
        { event: "resume", to: "running" },
        { event: "interrupt", to: "interrupted" },
        { event: "fail", to: "failed" }
      ];
    case "submitted":
      return [
        { event: "accept", to: "accepted" },
        { event: "reject-resume", to: "running" },
        { event: "reject-terminal", to: "rejected" }
      ];
    case "rejected":
      return [];
    default:
      return [];
  }
}
function resolveSubmitRouting(mode, decision) {
  if (!isAcceptMode(mode)) {
    throw new TaskLifecycleError(
      "INVALID_ACCEPT_MODE",
      `Invalid acceptMode: ${String(mode)}.`
    );
  }
  if (mode === "auto-accept") {
    return { autoIntegrate: true, integrationMode: "auto-accept", enterSubmitted: false };
  }
  if (mode === "review-required") {
    if (decision === "integrate") {
      throw new TaskLifecycleError(
        "POLICY_FORBIDS_AUTO_INTEGRATE",
        "acceptMode=review-required forbids decision=integrate; use request-review or change mode."
      );
    }
    return { autoIntegrate: false, integrationMode: null, enterSubmitted: true };
  }
  if (!decision) {
    throw new TaskLifecycleError(
      "DECISION_REQUIRED",
      "acceptMode=agent-decide requires decision: integrate | request-review."
    );
  }
  if (decision === "integrate") {
    return {
      autoIntegrate: true,
      integrationMode: "agent-decided-integrate",
      enterSubmitted: false
    };
  }
  return { autoIntegrate: false, integrationMode: null, enterSubmitted: true };
}
function assertReviewAuthority(input) {
  const actor = input.actor.trim();
  const executorRoleId = input.executorRoleId?.trim();
  const action = input.action ?? "accept";
  if (!actor) {
    throw new TaskLifecycleError(
      "REVIEW_FORBIDDEN",
      `task.${action} requires a non-empty actor.`
    );
  }
  if (executorRoleId && actor === executorRoleId) {
    throw new TaskLifecycleError(
      "SELF_ACCEPT_FORBIDDEN",
      `task.${action} actor must not equal executing Role (${executorRoleId}).`
    );
  }
  const requester = input.requester;
  if (!requester) {
    throw new TaskLifecycleError(
      "REVIEW_FORBIDDEN",
      `task.${action} requires an explicit Task.requester.`
    );
  }
  if (requester.kind === "user") {
    if (actor === requester.id && actor === "user") return;
    throw new TaskLifecycleError(
      "REVIEW_FORBIDDEN",
      `task.${action} on user-reviewed task requires actor user; got ${actor}.`
    );
  }
  if (actor === requester.id) return;
  throw new TaskLifecycleError(
    "REVIEW_FORBIDDEN",
    `task.${action} requires actor equal to requester role (${requester.id}); got ${actor}.`
  );
}

// src/core/canonical-digest.ts
import { createHash as createHash2 } from "node:crypto";
function sortForCanonical(value) {
  if (value === null || typeof value !== "object") return value;
  if (Array.isArray(value)) return value.map(sortForCanonical);
  const object = value;
  const sorted = {};
  for (const key2 of Object.keys(object).sort(compareCodeUnits)) {
    const item = object[key2];
    if (item === void 0) continue;
    sorted[key2] = sortForCanonical(item);
  }
  return sorted;
}
function compareCodeUnits(left, right) {
  return left < right ? -1 : left > right ? 1 : 0;
}
function canonicalJson(value) {
  return JSON.stringify(sortForCanonical(value));
}
function sha256Hex(text3) {
  return createHash2("sha256").update(text3, "utf8").digest("hex");
}
function canonicalSha256(value) {
  return sha256Hex(canonicalJson(value));
}

// src/core/task-node-selection.ts
var TaskNodeSelectionError = class extends Error {
  constructor(message2) {
    super(message2);
    this.name = "TaskNodeSelectionError";
  }
};
function normalizeNodeIds(value, field) {
  if (!Array.isArray(value)) {
    throw new TaskNodeSelectionError(`Task ${field} must be an array.`);
  }
  const ids = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of value) {
    if (typeof item !== "string" || item !== item.trim() || item !== item.toLowerCase() || !isNodeId(item)) {
      throw new TaskNodeSelectionError(
        `Task ${field} must contain canonical lowercase cx-* Node ids.`
      );
    }
    if (seen.has(item)) {
      throw new TaskNodeSelectionError(`Task ${field} contains duplicate Node id: ${item}.`);
    }
    seen.add(item);
    ids.push(item);
  }
  return ids;
}
function normalizeTaskNodeSelection(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TaskNodeSelectionError("Task Node selection must be an object.");
  }
  const record = value;
  const expected = /* @__PURE__ */ new Set(["nodeIds"]);
  if (Object.keys(record).some((key2) => !expected.has(key2))) {
    throw new TaskNodeSelectionError("Task Node selection contains unknown fields.");
  }
  const nodeIds = normalizeNodeIds(record.nodeIds, "nodeIds");
  return { nodeIds };
}
function orderedTaskNodeIds(selection) {
  const normalized = normalizeTaskNodeSelection(selection);
  return [...normalized.nodeIds];
}

// src/core/task-node-snapshot.ts
var TaskNodeSnapshotError = class extends Error {
  constructor(message2) {
    super(message2);
    this.name = "TaskNodeSnapshotError";
  }
};
function normalizeNodePath(value) {
  const path23 = value.trim().replace(/\\/g, "/").replace(/^\.\//, "");
  if (!path23 || path23.startsWith("/") || /^[a-zA-Z]:/.test(path23) || path23.split("/").some((segment) => segment === ".." || segment === "")) {
    throw new TaskNodeSnapshotError("Task Node snapshot path must be a canonical relative Node path.");
  }
  return path23;
}
function normalizeTags2(value) {
  if (!Array.isArray(value)) {
    throw new TaskNodeSnapshotError("Task Node snapshot tags must be an array.");
  }
  const tags = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of value) {
    if (typeof item !== "string" || !item.trim()) {
      throw new TaskNodeSnapshotError("Task Node snapshot tags must contain non-empty strings.");
    }
    const tag = item.trim();
    if (seen.has(tag)) continue;
    seen.add(tag);
    tags.push(tag);
  }
  return tags;
}
function normalizeTaskNodeSnapshot(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TaskNodeSnapshotError("Task Node snapshot must be an object.");
  }
  const record = value;
  const expected = /* @__PURE__ */ new Set(["id", "path", "type", "tags", "archived", "body", "etag"]);
  if (Object.keys(record).some((key2) => !expected.has(key2))) {
    throw new TaskNodeSnapshotError("Task Node snapshot contains unknown fields.");
  }
  if (typeof record.id !== "string" || record.id !== record.id.trim() || record.id !== record.id.toLowerCase() || !isNodeId(record.id)) {
    throw new TaskNodeSnapshotError("Task Node snapshot id must be a canonical cx-* Node id.");
  }
  if (typeof record.path !== "string") {
    throw new TaskNodeSnapshotError("Task Node snapshot path must be a string.");
  }
  if (typeof record.type !== "string" || !record.type.trim()) {
    throw new TaskNodeSnapshotError("Task Node snapshot type must be a non-empty string.");
  }
  if (typeof record.body !== "string") {
    throw new TaskNodeSnapshotError("Task Node snapshot body must be a string.");
  }
  if (typeof record.archived !== "boolean") {
    throw new TaskNodeSnapshotError("Task Node snapshot archived must be a boolean.");
  }
  if (typeof record.etag !== "string" || !/^[a-f0-9]{24}$/.test(record.etag)) {
    throw new TaskNodeSnapshotError("Task Node snapshot etag must be a canonical content etag.");
  }
  return {
    id: record.id.trim(),
    path: normalizeNodePath(record.path),
    type: record.type.trim(),
    tags: normalizeTags2(record.tags),
    archived: record.archived,
    body: record.body,
    etag: record.etag
  };
}
function captureTaskNodeSnapshot(node2, etag) {
  return normalizeTaskNodeSnapshot({
    id: node2.id,
    path: node2.path,
    type: node2.type,
    tags: node2.tags,
    archived: node2.archived,
    body: node2.body,
    etag
  });
}
function normalizeTaskNodeSnapshots(value, selection) {
  if (!Array.isArray(value)) {
    throw new TaskNodeSnapshotError("Task Node snapshots must be an array.");
  }
  const snapshots = value.map(normalizeTaskNodeSnapshot);
  const rootIds = orderedTaskNodeIds(selection);
  const seen = /* @__PURE__ */ new Set();
  for (const snapshot of snapshots) {
    if (seen.has(snapshot.id)) {
      throw new TaskNodeSnapshotError(`Task Node snapshots contain duplicate Node id: ${snapshot.id}.`);
    }
    seen.add(snapshot.id);
  }
  if (rootIds.length === 0) {
    if (snapshots.length !== 0) {
      throw new TaskNodeSnapshotError(
        "Prompt-only Task Node snapshots must be empty when nodeIds is empty."
      );
    }
    return snapshots;
  }
  const snapshotIndexById = new Map(snapshots.map((snapshot, index2) => [snapshot.id, index2]));
  const rootPaths = /* @__PURE__ */ new Map();
  let previousRootIndex = -1;
  for (const rootId of rootIds) {
    const index2 = snapshotIndexById.get(rootId);
    if (index2 === void 0) {
      throw new TaskNodeSnapshotError(
        `Task Node snapshots must include every selected root Node id: ${rootId}.`
      );
    }
    if (index2 <= previousRootIndex) {
      throw new TaskNodeSnapshotError(
        "Task Node snapshots must preserve the ordered root nodeIds[] sequence."
      );
    }
    rootPaths.set(rootId, snapshots[index2].path);
    previousRootIndex = index2;
  }
  const selectedRootPaths = [...rootPaths.values()];
  for (const snapshot of snapshots) {
    const underSelectedRoot = selectedRootPaths.some(
      (rootPath) => snapshot.path === rootPath || snapshot.path.startsWith(`${rootPath}/`)
    );
    if (!underSelectedRoot) {
      throw new TaskNodeSnapshotError(
        `Task Node snapshots must stay within the selected root subtrees: ${snapshot.path}.`
      );
    }
  }
  return snapshots;
}

// src/core/task-node-context.ts
var TaskNodeContextError = class extends Error {
  constructor(message2, cause) {
    super(message2);
    this.name = "TaskNodeContextError";
    this.cause = cause;
  }
};
function normalizeTaskNodeContext(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TaskNodeContextError("Task Node context must be an object.");
  }
  const record = value;
  const expected = /* @__PURE__ */ new Set(["nodeIds", "nodeSnapshots"]);
  if (Object.keys(record).some((key2) => !expected.has(key2))) {
    throw new TaskNodeContextError("Task Node context contains unknown fields.");
  }
  try {
    const selection = normalizeTaskNodeSelection({
      nodeIds: record.nodeIds
    });
    return {
      ...selection,
      nodeSnapshots: normalizeTaskNodeSnapshots(record.nodeSnapshots, selection)
    };
  } catch (error) {
    throw new TaskNodeContextError(
      error instanceof Error ? error.message : "Invalid Task Node context.",
      error
    );
  }
}

// src/core/task-context-card-schema.ts
var TASK_CONTEXT_CARD_SCHEMA_VERSION = "v3";
var TaskContextCardSchemaError = class extends Error {
  constructor(message2, cause) {
    super(message2);
    this.name = "TaskContextCardSchemaError";
    this.cause = cause;
  }
};
function normalizeTaskContextCard(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new TaskContextCardSchemaError("Task Context Card must be an object.");
  }
  const record = value;
  const expected = /* @__PURE__ */ new Set([
    "schemaVersion",
    "nodeIds",
    "nodeSnapshots",
    "contextGeneration"
  ]);
  if (Object.keys(record).some((key2) => !expected.has(key2))) {
    throw new TaskContextCardSchemaError("Task Context Card contains retired or unknown fields.");
  }
  if (record.schemaVersion !== TASK_CONTEXT_CARD_SCHEMA_VERSION) {
    throw new TaskContextCardSchemaError(
      `Task Context Card schemaVersion must be ${TASK_CONTEXT_CARD_SCHEMA_VERSION}.`
    );
  }
  if (record.contextGeneration !== void 0 && (typeof record.contextGeneration !== "string" || !/^cg-v1-[a-f0-9]{64}$/.test(record.contextGeneration))) {
    throw new TaskContextCardSchemaError(
      "Task Context Card contextGeneration must be a canonical cg-v1 digest when present."
    );
  }
  try {
    const nodeContext = normalizeTaskNodeContext({
      nodeIds: record.nodeIds,
      nodeSnapshots: record.nodeSnapshots
    });
    return {
      schemaVersion: TASK_CONTEXT_CARD_SCHEMA_VERSION,
      ...nodeContext,
      ...record.contextGeneration !== void 0 ? { contextGeneration: record.contextGeneration } : {}
    };
  } catch (error) {
    throw new TaskContextCardSchemaError(
      error instanceof Error ? error.message : "Invalid Task Node context.",
      error
    );
  }
}
function buildTaskContextCardRecord(input) {
  const nodeContext = normalizeTaskNodeContext({
    nodeIds: input.nodeIds,
    nodeSnapshots: input.nodeSnapshots
  });
  return normalizeTaskContextCard({
    schemaVersion: TASK_CONTEXT_CARD_SCHEMA_VERSION,
    ...nodeContext,
    ...input.contextGeneration ? { contextGeneration: input.contextGeneration } : {}
  });
}
function serializeTaskContextCard(card) {
  const normalized = normalizeTaskContextCard(card);
  return {
    schemaVersion: normalized.schemaVersion,
    nodeIds: [...normalized.nodeIds],
    nodeSnapshots: normalized.nodeSnapshots.map((snapshot) => ({
      ...snapshot,
      tags: [...snapshot.tags]
    })),
    ...normalized.contextGeneration ? { contextGeneration: normalized.contextGeneration } : {}
  };
}
function formatTaskContextCardMarkdown(card) {
  const normalized = normalizeTaskContextCard(card);
  const lines = [
    "Tent Task Context Card v3",
    `nodeIds: ${normalized.nodeIds.join(", ") || "(none)"}`,
    ...normalized.contextGeneration ? [`contextGeneration: ${normalized.contextGeneration}`] : []
  ];
  for (const snapshot of normalized.nodeSnapshots) {
    lines.push(
      "",
      `--- Node ${snapshot.id} ---`,
      `path: ${snapshot.path}`,
      `type: ${snapshot.type}`,
      `archived: ${snapshot.archived ? "true" : "false"}`,
      `tags: ${snapshot.tags.join(", ") || "(none)"}`,
      `etag: ${snapshot.etag}`,
      "body:",
      snapshot.body
    );
  }
  return lines.join("\n").trimEnd();
}

// src/core/task-context-card.ts
var CONTEXT_GENERATION_VERSION = "v1";
var MANAGED_BOOTSTRAP_INVARIANT = "Tent managed bootstrap invariant v1: Core is authoritative. Fetch by durable id before answering. Never invent missing Context Card fields, Task authority, Node context, or chat-memory continuity. Final report goes through TaskResult only.";
var INTEGRATION_MUTATOR_SERVICE = "service";
var CONTEXT_GENERATION_FORBIDDEN_EXTRA_KEYS = [
  "taskId",
  "taskPath",
  "objective",
  "acceptance",
  "prompt",
  "taskInputDelta"
];
var TaskContextCardError = class extends Error {
  constructor(code, message2, details) {
    super(message2);
    this.name = "TaskContextCardError";
    this.code = code;
    this.details = details;
  }
};
function formatContextGeneration(stableCanonicalBytes) {
  return `cg-${CONTEXT_GENERATION_VERSION}-${sha256Hex(stableCanonicalBytes)}`;
}
function isContextGenerationId(value) {
  return typeof value === "string" && /^cg-v1-[a-f0-9]{64}$/.test(value);
}
function computeContextGeneration(inputs) {
  const extraStable = sanitizeContextGenerationExtraStable(inputs.extraStable);
  const payload = {
    v: CONTEXT_GENERATION_VERSION,
    workspaceIdentity: inputs.workspaceIdentity.trim(),
    agentsPointerDigest: inputs.agentsPointerDigest.trim(),
    tentRoleDigest: inputs.tentRoleDigest?.trim() || "",
    tentRoleVersion: inputs.tentRoleVersion?.trim() || "",
    rolePrompt: inputs.rolePrompt?.trim() || "",
    tentTaskDigest: inputs.tentTaskDigest?.trim() || "",
    tentTaskVersion: inputs.tentTaskVersion?.trim() || "",
    connectionAdapterCompatibility: inputs.connectionAdapterCompatibility?.trim() || "",
    extraStable
  };
  return formatContextGeneration(canonicalJson(payload));
}
function sanitizeContextGenerationExtraStable(extra) {
  const out = {};
  if (!extra) return out;
  const forbidden = new Set(
    CONTEXT_GENERATION_FORBIDDEN_EXTRA_KEYS.map((k) => k.toLowerCase())
  );
  for (const key2 of Object.keys(extra).sort((a, b) => a.localeCompare(b))) {
    if (forbidden.has(key2.toLowerCase())) continue;
    const v = extra[key2];
    if (v === void 0) continue;
    out[key2] = v;
  }
  return out;
}
function agentsBodyCompatibilityDigest(agentsBody) {
  return sha256Hex(typeof agentsBody === "string" ? agentsBody : "");
}
function skillBodyCompatibilityDigest(input) {
  return sha256Hex(
    canonicalJson({
      name: input.name?.trim() || "",
      version: input.version?.trim() || "",
      body: input.body
    })
  );
}
function computeContextGenerationFromStableFacts(input) {
  const tentRoleDigest = input.tentRoleBody !== void 0 ? skillBodyCompatibilityDigest({
    body: input.tentRoleBody,
    version: input.tentRoleVersion,
    name: "tent-role"
  }) : "";
  const tentTaskDigest = input.tentTaskBody !== void 0 ? skillBodyCompatibilityDigest({
    body: input.tentTaskBody,
    version: input.tentTaskVersion,
    name: "tent-task"
  }) : "";
  return computeContextGeneration({
    workspaceIdentity: input.workspaceIdentity,
    agentsPointerDigest: input.agentsPointerDigest?.trim() || agentsBodyCompatibilityDigest(input.agentsBody),
    tentRoleDigest: tentRoleDigest || void 0,
    tentRoleVersion: input.tentRoleVersion,
    rolePrompt: input.rolePrompt,
    tentTaskDigest: tentTaskDigest || void 0,
    tentTaskVersion: input.tentTaskVersion,
    connectionAdapterCompatibility: connectionAdapterCompatibilityDigest({
      connectionId: input.connectionId,
      adapterId: input.adapterId,
      capabilityFlags: input.capabilityFlags,
      launchDigest: input.connectionLaunchDigest
    }),
    extraStable: {
      ...input.roleId ? { roleId: input.roleId } : {},
      ...input.connectionLaunchDigest?.trim() ? { connectionLaunchDigest: input.connectionLaunchDigest.trim() } : {},
      ...input.extraStable
    }
  });
}
function buildTaskContextCard(input) {
  const retired = [
    "objective",
    "frozenDecisions",
    "scope",
    "acceptance",
    "refs"
  ];
  const record = input;
  const retiredField = retired.find((field) => field in record);
  if (retiredField) {
    throw new TaskContextCardError(
      "INVALID_CARD",
      `Task Context Card field ${retiredField} is retired; use frozen Node snapshots.`,
      { retiredField }
    );
  }
  if (input.contextGeneration && !isContextGenerationId(input.contextGeneration)) {
    throw new TaskContextCardError(
      "INVALID_GENERATION",
      `contextGeneration must match cg-v1-<sha256>; got ${String(input.contextGeneration)}`
    );
  }
  try {
    return buildTaskContextCardRecord(input);
  } catch (error) {
    if (error instanceof TaskContextCardError) throw error;
    throw new TaskContextCardError(
      "INVALID_CARD",
      error instanceof Error ? error.message : "Invalid Task Context Card.",
      { cause: error }
    );
  }
}
function parseTaskContextCard(data) {
  try {
    return normalizeTaskContextCard(data);
  } catch (error) {
    throw new TaskContextCardError(
      "INVALID_CARD",
      error instanceof Error ? error.message : "Invalid Task Context Card.",
      { cause: error }
    );
  }
}
function loadTaskContextCardFromFrontmatter(data) {
  if (data.contextCard !== void 0 && data.contextCard !== null) {
    return parseTaskContextCard(data.contextCard);
  }
  return null;
}
function serializeTaskContextCardForFrontmatter(card) {
  return serializeTaskContextCard(card);
}
function formatTaskContextCardPrompt(card) {
  return formatTaskContextCardMarkdown(card);
}
function formatTaskPackage(input) {
  const card = parseTaskContextCard(input.contextCard);
  const packageCard = { ...card };
  delete packageCard.contextGeneration;
  const prompt = input.prompt?.trim() || "";
  if (!prompt) {
    throw new TaskContextCardError(
      "INVALID_CARD",
      "Task Package requires a non-empty prompt."
    );
  }
  const parts = [
    "--- Tent Task Package ---",
    formatTaskContextCardPrompt(packageCard)
  ];
  if (input.taskPointers?.trim()) {
    parts.push("", input.taskPointers.trim());
  }
  parts.push(
    "",
    "## Prompt",
    "",
    prompt
  );
  return parts.join("\n");
}
function formatStableProjectContext(input) {
  return [
    "Tent stable project context v1",
    `workspaceRoot: ${input.workspaceRoot}`,
    `systemRoot: ${input.systemRoot}`,
    `AGENTS: ${input.agentsPointer}`,
    "CLI: run tent from workspaceRoot; taskPath is relative to systemRoot (.tent).",
    "Do not resolve operational files as <workspaceRoot>/temp \u2014 use .tent/temp."
  ].join("\n");
}
function formatDynamicDelta(input) {
  const taskPackage2 = input.taskPackage ?? "";
  if (!taskPackage2.trim()) {
    throw new TaskContextCardError(
      "INVALID_CARD",
      "Managed prompt assembly requires a non-empty canonical Task Package."
    );
  }
  const wrapper = input.dynamicWrapper?.trim();
  if (!wrapper) {
    return { taskPackage: taskPackage2, dynamicDelta: taskPackage2 };
  }
  return {
    taskPackage: taskPackage2,
    dynamicDelta: `${taskPackage2}

${wrapper}`
  };
}
function assembleManagedPrompt(input) {
  if (!isContextGenerationId(input.contextGeneration)) {
    throw new TaskContextCardError(
      "INVALID_GENERATION",
      `contextGeneration must match cg-v1-<sha256>; got ${String(input.contextGeneration)}`
    );
  }
  const includeStablePrefix = input.includeStablePrefix !== false;
  const { taskPackage: taskPackage2, dynamicDelta } = formatDynamicDelta(input);
  if (!includeStablePrefix) {
    return {
      text: dynamicDelta + "\n",
      contextGeneration: input.contextGeneration,
      includedStablePrefix: false,
      stablePrefix: "",
      taskPackage: taskPackage2,
      dynamicDelta
    };
  }
  const stableParts = [
    MANAGED_BOOTSTRAP_INVARIANT,
    formatStableProjectContext(input)
  ];
  if (input.tentRoleSection?.trim()) {
    stableParts.push(input.tentRoleSection.trim());
  }
  if (input.rolePromptSection?.trim()) {
    stableParts.push(input.rolePromptSection.trim());
  }
  if (input.tentTaskSection?.trim()) {
    stableParts.push(input.tentTaskSection.trim());
  }
  const stablePrefix = stableParts.join("\n\n");
  const text3 = `${stablePrefix}

${dynamicDelta}
`;
  return {
    text: text3,
    contextGeneration: input.contextGeneration,
    includedStablePrefix: true,
    stablePrefix,
    taskPackage: taskPackage2,
    dynamicDelta
  };
}
function decideStablePrefixInjection(input) {
  const currentGen = input.currentContextGeneration?.trim() || "";
  if (!isContextGenerationId(currentGen)) {
    return {
      includeStablePrefix: true,
      reason: "current_context_generation_invalid_or_missing"
    };
  }
  const prior = input.sessionContextGeneration?.trim() || "";
  if (!prior) {
    return { includeStablePrefix: true, reason: "session_has_no_context_generation" };
  }
  if (!isContextGenerationId(prior)) {
    return {
      includeStablePrefix: true,
      reason: "session_context_generation_invalid"
    };
  }
  if (prior !== currentGen) {
    return {
      includeStablePrefix: true,
      reason: "context_generation_mismatch"
    };
  }
  return {
    includeStablePrefix: false,
    reason: "same_context_generation"
  };
}
function shouldInjectStablePrefix(input) {
  return decideStablePrefixInjection(input).includeStablePrefix;
}
function connectionAdapterCompatibilityDigest(input) {
  const flags = [...input.capabilityFlags ?? []].map((s) => s.trim()).filter(Boolean).sort((a, b) => a.localeCompare(b));
  return sha256Hex(
    canonicalJson({
      connectionId: input.connectionId.trim(),
      adapterId: input.adapterId.trim(),
      flags,
      launchDigest: input.launchDigest?.trim() || ""
    })
  );
}
function deriveIntegrationAuthority(input) {
  try {
    const requester = parseTaskActorRef(input.requester, "requester");
    return {
      actor: { kind: requester.kind, id: requester.id },
      mutator: INTEGRATION_MUTATOR_SERVICE
    };
  } catch (err) {
    if (err instanceof TaskLifecycleError) {
      throw new TaskContextCardError("INVALID_ACTOR", err.message, {
        requester: input.requester
      });
    }
    throw err;
  }
}
function assertIntegrationAuthorityMatchesParent(authority, requester) {
  const derived = deriveIntegrationAuthority({ requester });
  if (!authority || typeof authority !== "object" || Array.isArray(authority)) {
    throw new TaskContextCardError(
      "INVALID_ACTOR",
      "integrationAuthority must be { actor, mutator: service } derived from requester.",
      { authority }
    );
  }
  const raw = authority;
  if (raw.mutator !== INTEGRATION_MUTATOR_SERVICE) {
    throw new TaskContextCardError(
      "INVALID_ACTOR",
      `integrationAuthority.mutator must be "${INTEGRATION_MUTATOR_SERVICE}" (Service only); got ${String(raw.mutator)}.`,
      { authority }
    );
  }
  let actor;
  try {
    actor = parseTaskActorRef(raw.actor, "requester");
  } catch (err) {
    if (err instanceof TaskLifecycleError) {
      throw new TaskContextCardError("INVALID_ACTOR", err.message, { authority });
    }
    throw err;
  }
  if (actor.kind !== derived.actor.kind || actor.id !== derived.actor.id) {
    throw new TaskContextCardError(
      "INVALID_ACTOR",
      `integrationAuthority.actor must equal Task requester (${derived.actor.kind}:${derived.actor.id}); got ${actor.kind}:${actor.id}.`,
      { authority, derived }
    );
  }
  return derived;
}
function projectExecutionLaneFromTask(task) {
  const baseCommit = typeof task.baseCommit === "string" && task.baseCommit.trim() ? task.baseCommit.trim() : "";
  const targetBranch = typeof task.targetBranch === "string" ? task.targetBranch.trim() : "";
  const branch = typeof task.branch === "string" ? task.branch.trim() : "";
  const worktree = typeof task.worktree === "string" ? task.worktree.trim() : "";
  let integrationAuthority;
  if (task.requester) {
    integrationAuthority = deriveIntegrationAuthority({ requester: task.requester });
  }
  if (!baseCommit && !targetBranch && !branch && !worktree && !integrationAuthority) {
    return void 0;
  }
  const out = {};
  if (baseCommit) out.baseCommit = baseCommit;
  if (targetBranch) out.targetBranch = targetBranch;
  if (branch) out.branch = branch;
  if (worktree) out.worktree = worktree;
  if (integrationAuthority) out.integrationAuthority = integrationAuthority;
  return out;
}
function formatExecutionLanePrompt(lane) {
  if (!lane) return "";
  const lines = ["executionLane (derived projection \u2014 Task workspaceLane is truth):"];
  if (lane.baseCommit) lines.push(`  baseCommit: ${lane.baseCommit}`);
  if (lane.targetBranch) lines.push(`  targetBranch: ${lane.targetBranch}`);
  if (lane.branch) lines.push(`  branch: ${lane.branch}`);
  if (lane.worktree) lines.push(`  worktree: ${lane.worktree}`);
  if (lane.integrationAuthority) {
    const a = lane.integrationAuthority.actor;
    lines.push(
      `  integrationAuthority.actor: ${a.kind}:${a.id}`,
      `  integrationAuthority.mutator: ${lane.integrationAuthority.mutator}`
    );
  }
  return lines.join("\n");
}
var ExecutorLaneHistoryError = class extends Error {
  constructor(code, message2, details) {
    super(message2);
    this.name = "ExecutorLaneHistoryError";
    this.code = code;
    this.details = details;
  }
};
function assertOrdinaryExecutorLaneHistory(input) {
  const base = input.baseCommit?.trim() || "";
  const tip = input.tipCommit?.trim() || "";
  if (!base) {
    throw new ExecutorLaneHistoryError(
      "MISSING_BASE",
      "Executor lane history gate requires recorded baseCommit (fail-loud; no ready TaskResult)."
    );
  }
  if (!tip) {
    throw new ExecutorLaneHistoryError(
      "MISSING_TIP",
      "Executor lane history gate requires tip commit (fail-loud; no ready TaskResult)."
    );
  }
  const commits = input.commits ?? [];
  if (commits.length === 0) {
    if (base !== tip) {
      throw new ExecutorLaneHistoryError(
        "TIP_MISMATCH",
        `Executor lane has no commits in base..tip but tip ${tip} !== base ${base}.`,
        { base, tip }
      );
    }
    return;
  }
  const first = commits[0];
  if (!first.sha?.trim()) {
    throw new ExecutorLaneHistoryError(
      "FOREIGN_ANCESTRY",
      "Executor lane history contains a commit with empty sha.",
      { index: 0 }
    );
  }
  if (!first.parents || first.parents.length === 0) {
    throw new ExecutorLaneHistoryError(
      "EMPTY_PARENT",
      `Executor lane commit ${first.sha} has no parents (unexpected root in task range).`,
      { sha: first.sha }
    );
  }
  if (first.parents.length !== 1) {
    throw new ExecutorLaneHistoryError(
      "MERGE_COMMIT",
      `Unauthorized merge commit on ordinary executor lane: ${first.sha} has ${first.parents.length} parents (no ready TaskResult; lane/audit preserved).`,
      { sha: first.sha, parents: first.parents }
    );
  }
  if (first.parents[0] !== base) {
    throw new ExecutorLaneHistoryError(
      "BASE_NOT_FIRST_PARENT",
      `Recorded baseCommit ${base} is not the exact first parent of the first Task commit ${first.sha} (parent=${first.parents[0]}). Unauthorized foreign ancestry; no ready TaskResult.`,
      { base, firstSha: first.sha, firstParent: first.parents[0] }
    );
  }
  let prev = first.sha;
  for (let i = 0; i < commits.length; i++) {
    const c = commits[i];
    const sha = c.sha?.trim() || "";
    if (!sha) {
      throw new ExecutorLaneHistoryError(
        "FOREIGN_ANCESTRY",
        `Executor lane history contains a commit with empty sha at index ${i}.`,
        { index: i }
      );
    }
    const parents = c.parents ?? [];
    if (parents.length === 0) {
      throw new ExecutorLaneHistoryError(
        "EMPTY_PARENT",
        `Executor lane commit ${sha} has no parents.`,
        { sha, index: i }
      );
    }
    if (parents.length !== 1) {
      throw new ExecutorLaneHistoryError(
        "MERGE_COMMIT",
        `Unauthorized merge commit on ordinary executor lane: ${sha} has ${parents.length} parents (executor must not merge parent/target/dependency; no ready TaskResult).`,
        { sha, parents, index: i }
      );
    }
    if (i > 0 && parents[0] !== prev) {
      throw new ExecutorLaneHistoryError(
        "FOREIGN_ANCESTRY",
        `Executor lane commit ${sha} first parent ${parents[0]} is not prior tip ${prev} (foreign ancestry / non-linear history; no ready TaskResult).`,
        { sha, firstParent: parents[0], expectedParent: prev, index: i }
      );
    }
    prev = sha;
  }
  if (prev !== tip) {
    throw new ExecutorLaneHistoryError(
      "TIP_MISMATCH",
      `Executor lane tip ${tip} does not match last commit in base..tip range ${prev}.`,
      { tip, last: prev, base }
    );
  }
}

// src/core/task.ts
async function loadTaskRecords(fs21) {
  const tasks = [];
  if (!await fs21.exists(TEMP_DIR)) return tasks;
  for (const entry of await fs21.listDir(TEMP_DIR)) {
    if (!entry.isDir) continue;
    if (entry.name !== ROLES_TEMP_DIR && entry.name !== SESSIONS_TEMP_DIR) continue;
    const ownerRoot = join(TEMP_DIR, entry.name);
    for (const ownerEntry of await fs21.listDir(ownerRoot)) {
      if (!ownerEntry.isDir) continue;
      await collectTaskFiles(fs21, join(ownerRoot, ownerEntry.name, "tasks"), tasks);
    }
  }
  return tasks.sort((a, b) => a.path.localeCompare(b.path));
}
async function collectTaskFiles(fs21, taskDir, tasks) {
  if (!await fs21.exists(taskDir)) return;
  for (const entry of await fs21.listDir(taskDir)) {
    if (entry.isDir || !entry.name.endsWith(".md")) continue;
    const path23 = join(taskDir, entry.name);
    tasks.push(await loadTaskRecord(fs21, path23));
  }
}
function taskParentRoleId(task) {
  return task.requester?.kind === "role" ? task.requester.id : void 0;
}
function serializeTaskActorRef(actor) {
  return { kind: actor.kind, id: actor.id };
}
function serializeBaseCommitCapture(capture) {
  return {
    source: capture.source,
    baseCommit: capture.baseCommit,
    actor: serializeTaskActorRef(capture.actor),
    capturedAt: capture.capturedAt
  };
}
function assertIsoTimestamp(value, label) {
  const raw = value.trim();
  if (!raw) {
    throw new Error(`${label} must be a non-empty ISO-8601 timestamp.`);
  }
  if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/.test(raw)) {
    throw new Error(
      `${label} must be a real ISO-8601 timestamp with timezone; got ${raw}.`
    );
  }
  const ms = Date.parse(raw);
  if (!Number.isFinite(ms)) {
    throw new Error(`${label} is not a parseable ISO-8601 instant: ${raw}.`);
  }
  return raw;
}
var TASK_STATUS_DETAIL_REPORT_MAX_BYTES = 64 * 1024;
var TASK_STATUS_DETAIL_ERROR_MAX_BYTES = 8 * 1024;
var TASK_STATUS_DETAIL_CODE_MAX_BYTES = 128;
function parseTaskStatusDetail(value) {
  if (value === void 0 || value === null) return void 0;
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new Error("Task statusDetail must be an object.");
  }
  const raw = value;
  const allowed = /* @__PURE__ */ new Set(["kind", "report", "error", "code", "at", "executionSessionId"]);
  if (Object.keys(raw).some((key2) => !allowed.has(key2))) {
    throw new Error("Task statusDetail contains unknown fields.");
  }
  const kind = raw.kind;
  if (kind !== "blocked" && kind !== "failed") {
    throw new Error("Task statusDetail.kind must be blocked or failed.");
  }
  const report = parseBoundedTaskStatusDetailString(
    raw.report,
    "report",
    TASK_STATUS_DETAIL_REPORT_MAX_BYTES
  );
  const error = parseBoundedTaskStatusDetailString(
    raw.error,
    "error",
    TASK_STATUS_DETAIL_ERROR_MAX_BYTES
  );
  if (!report && !error) {
    throw new Error("Task statusDetail requires report or error.");
  }
  const code = parseBoundedTaskStatusDetailString(
    raw.code,
    "code",
    TASK_STATUS_DETAIL_CODE_MAX_BYTES
  );
  if (code && !/^[A-Za-z0-9_.:-]+$/.test(code)) {
    throw new Error("Task statusDetail.code must be a stable machine identifier.");
  }
  const at = raw.at === void 0 ? void 0 : assertIsoTimestamp(String(raw.at), "Task statusDetail.at");
  const executionSessionId = raw.executionSessionId === void 0 ? void 0 : String(raw.executionSessionId).trim();
  if (executionSessionId && !isSessionId(executionSessionId)) {
    throw new Error(`Invalid Task statusDetail.executionSessionId: ${executionSessionId}.`);
  }
  return {
    kind,
    ...report ? { report } : {},
    ...error ? { error } : {},
    ...code ? { code } : {},
    ...at ? { at } : {},
    ...executionSessionId ? { executionSessionId } : {}
  };
}
function parseBoundedTaskStatusDetailString(value, field, maxBytes) {
  if (value === void 0 || value === null) return void 0;
  if (typeof value !== "string") {
    throw new Error(`Task statusDetail.${field} must be a string.`);
  }
  const text3 = value.trim();
  if (!text3) return void 0;
  const bytes = Buffer2.byteLength(text3, "utf8");
  if (bytes > maxBytes) {
    throw new Error(`Task statusDetail.${field} exceeds ${maxBytes} UTF-8 bytes.`);
  }
  return text3;
}
function parseBaseCommitCapture(value) {
  if (value === void 0 || value === null) return void 0;
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new Error(
      "Task baseCommitCapture must be an object { source, baseCommit, actor, capturedAt }."
    );
  }
  const raw = value;
  const source = raw.source;
  if (source !== "first-claim") {
    throw new Error(
      `Task baseCommitCapture.source must be first-claim; got ${String(source)}.`
    );
  }
  const baseCommit = typeof raw.baseCommit === "string" ? raw.baseCommit.trim() : "";
  if (!baseCommit) {
    throw new Error("Task baseCommitCapture.baseCommit must be a non-empty SHA.");
  }
  const capturedAtRaw = typeof raw.capturedAt === "string" ? raw.capturedAt.trim() : "";
  const capturedAt = assertIsoTimestamp(
    capturedAtRaw,
    "Task baseCommitCapture.capturedAt"
  );
  let actor;
  try {
    actor = parseTaskActorRef(raw.actor, "requester");
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    throw new Error(msg.replace(/Task requester/g, "Task baseCommitCapture.actor"));
  }
  return { source, baseCommit, actor, capturedAt };
}
function resolveDispatchRequester(input) {
  if (!input.requester) {
    throw new Error(
      "task.dispatch requires explicit requester { kind, id }."
    );
  }
  return parseTaskActorRef(input.requester, "requester");
}
async function loadTaskRecord(fs21, path23) {
  if (!await fs21.exists(path23)) throw new Error(`Task record not found: ${path23}.`);
  const { data, body } = parseFrontmatter(await fs21.readFile(path23));
  assertNoRetiredTaskAuthorityFields(data);
  if (data.type !== "task" || typeof data.manifest !== "string") {
    throw new Error(`Invalid task record format: ${path23}.`);
  }
  const assigneeRoleId = typeof data.assigneeRoleId === "string" ? data.assigneeRoleId.trim() : "";
  const executionSessionId = typeof data.executionSessionId === "string" ? data.executionSessionId.trim() : "";
  if (!assigneeRoleId && !executionSessionId) {
    throw new Error(`Invalid Task record format: ${path23} (assigneeRoleId or executionSessionId is required).`);
  }
  if (assigneeRoleId && !isRoleId(assigneeRoleId)) {
    throw new Error(`Invalid Task record format: ${path23} (invalid assigneeRoleId).`);
  }
  if (executionSessionId && !isSessionId(executionSessionId)) {
    throw new Error(`Invalid Task record format: ${path23} (invalid executionSessionId).`);
  }
  const normalizedTaskPath = path23.replace(/\\/g, "/");
  const authoritativeOwnerDir = assigneeRoleId ? `temp/${ROLES_TEMP_DIR}/${assigneeRoleId}/tasks/` : void 0;
  const sessionOwnerMatch = normalizedTaskPath.match(
    new RegExp(`^temp/${SESSIONS_TEMP_DIR}/([^/]+)/tasks/`)
  );
  const ownerIsValid = assigneeRoleId ? normalizedTaskPath.startsWith(authoritativeOwnerDir) : !!sessionOwnerMatch && isSessionId(sessionOwnerMatch[1]);
  if (!ownerIsValid) {
    throw new Error(
      `Invalid Task record owner namespace: ${path23}.`
    );
  }
  if (typeof data.id !== "string" || !isTaskId(data.id)) {
    throw new Error(`Invalid task record format: ${path23} (canonical task id is required).`);
  }
  const state = parseTaskState(data.state);
  const requester = resolveRequesterFromDisk(data);
  const contextCard = loadTaskContextCardFromFrontmatter(data) ?? void 0;
  if (!contextCard) {
    throw new Error(
      `Invalid task record format: ${path23} (missing Task Context Card).`
    );
  }
  if (!isAcceptMode(data.acceptMode)) {
    throw new Error(
      `Invalid task record format: ${path23} (acceptMode must be review-required, auto-accept, or agent-decide).`
    );
  }
  const prompt = body.trim();
  if (!prompt) {
    throw new Error(`Invalid Task record format: ${path23} (non-empty prompt is required).`);
  }
  const task = {
    path: path23,
    ...assigneeRoleId ? { assigneeRoleId } : {},
    ...executionSessionId ? { executionSessionId } : {},
    manifest: data.manifest,
    state,
    id: data.id,
    requester,
    prompt,
    contextCard,
    nodeIds: contextCard.nodeIds,
    nodeSnapshots: contextCard.nodeSnapshots,
    acceptMode: data.acceptMode
  };
  if (typeof data.workspace === "string") task.workspace = data.workspace;
  if (typeof data.worktree === "string") task.worktree = data.worktree;
  if (typeof data.branch === "string") task.branch = data.branch;
  if (typeof data.targetBranch === "string") task.targetBranch = data.targetBranch;
  if (typeof data.roleBranchBase === "string" && data.roleBranchBase.trim()) {
    task.roleBranchBase = data.roleBranchBase.trim();
  }
  if (typeof data.baseCommit === "string" && data.baseCommit.trim()) {
    task.baseCommit = data.baseCommit.trim();
  }
  const baseCommitCapture = parseBaseCommitCapture(data.baseCommitCapture);
  if (baseCommitCapture) {
    const recordedBase = task.baseCommit?.trim() || "";
    if (!recordedBase) {
      throw new Error(
        `Invalid task record format: ${path23} (baseCommitCapture present but baseCommit missing).`
      );
    }
    if (recordedBase !== baseCommitCapture.baseCommit) {
      throw new Error(
        `Invalid task record format: ${path23} (baseCommit ${recordedBase} !== baseCommitCapture.baseCommit ${baseCommitCapture.baseCommit}).`
      );
    }
    task.baseCommitCapture = baseCommitCapture;
  }
  if (data.integrationAuthority !== void 0 && data.integrationAuthority !== null && task.requester) {
    task.integrationAuthority = assertIntegrationAuthorityMatchesParent(
      data.integrationAuthority,
      task.requester
    );
  }
  task.contextGeneration = contextCard.contextGeneration;
  if (data.currentResultId !== void 0) {
    if (typeof data.currentResultId !== "string" || data.currentResultId !== data.currentResultId.trim() || !isTaskResultId(data.currentResultId)) {
      throw new Error(`Invalid Task currentResultId: ${String(data.currentResultId)}.`);
    }
    task.currentResultId = data.currentResultId;
  }
  const statusDetail = parseTaskStatusDetail(data.statusDetail);
  if (statusDetail) task.statusDetail = statusDetail;
  if (typeof data.createdAt === "string") task.createdAt = data.createdAt;
  if (typeof data.updatedAt === "string") task.updatedAt = data.updatedAt;
  const wait = parseWaitFields(data);
  if (wait) task.wait = wait;
  return task;
}
function resolveRequesterFromDisk(data) {
  if (data.requester === void 0 || data.requester === null) {
    throw new Error("Invalid Task record: missing requester.");
  }
  return parseTaskActorRef(data.requester, "requester");
}
function assertNoRetiredTaskAuthorityFields(data) {
  for (const field of ["reviewer", "dispatchedBy"]) {
    if (Object.prototype.hasOwnProperty.call(data, field)) {
      throw new Error(`Invalid Task record: retired ${field} field; use requester.`);
    }
  }
}
function resolveTaskPromptRoots(roots) {
  if (typeof roots !== "string") {
    return {
      workspaceRoot: roots.workspaceRoot,
      systemRoot: roots.systemRoot
    };
  }
  const systemRoot = roots;
  const normalized = systemRoot.replace(/[\\/]+$/, "");
  const base = normalized.split(/[\\/]/).pop() ?? "";
  const workspaceRoot = base === ".tent" ? normalized.replace(/[\\/]+[^\\/]+$/, "") || systemRoot : systemRoot;
  return { workspaceRoot, systemRoot };
}
function formatTaskPackagePointers(task) {
  const lines = [
    `Task record: ${task.path}`,
    `Manifest: ${task.manifest}`
  ];
  if (task.id) {
    lines.push(`Task id: ${task.id}`);
  }
  lines.push(`nodeIds: ${task.nodeIds.join(", ") || "(none)"}`);
  if (task.requester) {
    lines.push(
      `requester: ${task.requester.kind}:${task.requester.id}`
    );
  }
  lines.push(`acceptMode: ${task.acceptMode}`);
  if (task.assigneeRoleId) {
    lines.push(`assigneeRoleId: ${task.assigneeRoleId}`);
  }
  if (!task.assigneeRoleId) lines.push(`Session-only execution (no durable Role responsibility).`);
  return lines.join("\n");
}
function taskPackageForTask(task) {
  const prompt = extractTaskPrompt(task);
  if (!prompt) throw new Error(`Task Package requires a non-empty prompt: ${task.path}.`);
  const pointerSections = [formatTaskPackagePointers(task)];
  const executionLane = formatExecutionLanePrompt(projectExecutionLaneFromTask(task));
  if (executionLane) {
    pointerSections.push(executionLane);
  }
  pointerSections.push(
    "TaskResult contract: a non-empty final report is the normal success path.",
    "When applicable, include commits, checks, and artifact refs in the same TaskResult.",
    "Never self-accept."
  );
  return formatTaskPackage({
    contextCard: task.contextCard,
    taskPointers: pointerSections.join("\n"),
    prompt
  });
}
function formatExternalPathBlock(task, roots) {
  const taskFile = join(".tent", task.path);
  const systemRoot = roots.systemRoot.replace(/[\\/]+$/, "");
  return [
    `workspaceRoot: ${roots.workspaceRoot}`,
    `systemRoot: ${roots.systemRoot}`,
    `CLI: run tent from workspaceRoot; taskPath is relative to systemRoot (.tent), e.g. ${task.path}.`,
    `File reads: use ${taskFile} (workspace-relative) or ${systemRoot}/${task.path} \u2014 never <workspaceRoot>/temp.`
  ].join("\n");
}
function relayPromptForTask(task, roots) {
  const resolved = resolveTaskPromptRoots(roots);
  const assigneeLine = task.assigneeRoleId ? `A Tent task has been handed to Role ${task.assigneeRoleId}.
` : `A Tent task is bound to Session ${task.executionSessionId}.
`;
  const initStep = task.assigneeRoleId ? `4. If this is a new session for this Role, complete Role init first (read the init file above).` : `4. Read the Task record and task-scoped manifest pointers above; no Role init applies.`;
  const roleInitBlock = task.assigneeRoleId ? `${join(".tent", "temp", ROLES_TEMP_DIR, task.assigneeRoleId, "init.md")}
` : "";
  return assigneeLine + `${formatExternalPathBlock(task, resolved)}
` + roleInitBlock + `1. Run \`tent task claim ${task.path}\` to take this task (Local Service RPC).
2. Read the frozen Task Context Card (\`tent task get ${task.path}\` or the Task record). Resolve current Node state by id only when comparing drift.
3. When finished, run \`tent task submit ${task.path} --report <text>\` (optional: --commits sha,sha).
${initStep}

` + taskPackageForTask(task);
}
function extractTaskPrompt(task) {
  const body = task.prompt?.trim() || "";
  if (!body) return "";
  const match = body.match(/##\s*Prompt\s*\r?\n+([\s\S]*?)\s*$/i);
  if (match) return match[1].trim();
  return body;
}
async function ensureRoleInit(fs21, role, tentName) {
  if (!role.id || !isRoleId(role.id)) {
    throw new Error(`Role init requires a canonical Role id for ${role.name}.`);
  }
  const path23 = join(TEMP_DIR, ROLES_TEMP_DIR, role.id, "init.md");
  const body = `# Role Init

- Tent: ${tentName}
- Agent rules (workspace file read): AGENTS.md at the workspace root
- Role registry (workspace file read): .tent/roles.json (or run \`tent roles\` from workspace root)

## Role Prompt

${role.prompt?.trim() || "(no persistent role prompt)"}

## Operating Model

Manifest readable/writable entries are an honor-system contract, not a security sandbox. If prompts conflict or a boundary cannot be followed, stop and ask the user.
Task lifecycle uses \`tent task *\` (Local Service). Do not invent paths as <workspace>/temp \u2014 operational files live under .tent/temp.
`;
  await fs21.writeFile(path23, serializeFrontmatter({ type: "role-init", role: role.name }, body));
  return path23;
}
async function writeTaskRecord(fs21, clock, input) {
  const prompt = input.prompt?.trim() || "";
  if (!prompt) throw new Error("Dispatch requires a prompt.");
  const assigneeRoleId = input.assigneeRoleId?.trim() || "";
  const executionSessionId = input.executionSessionId?.trim() || "";
  if (!assigneeRoleId && !executionSessionId) {
    throw new Error("Task requires assigneeRoleId or executionSessionId at creation.");
  }
  if (assigneeRoleId && !isRoleId(assigneeRoleId)) throw new Error(`Invalid Task assigneeRoleId: ${assigneeRoleId}.`);
  if (executionSessionId && !isSessionId(executionSessionId)) {
    throw new Error(`Invalid Task executionSessionId: ${executionSessionId}.`);
  }
  const dir = assigneeRoleId ? roleTasksDir(assigneeRoleId) : sessionTasksDir(executionSessionId);
  await ensureDir(fs21, dir);
  const requestedId = input.id?.trim() || "";
  if (requestedId && !isTaskId(requestedId)) {
    throw new Error(`Invalid Task id: ${requestedId}.`);
  }
  const id = requestedId || makeTaskId();
  const nodeContext = normalizeTaskNodeContext({
    nodeIds: input.nodeIds,
    nodeSnapshots: input.nodeSnapshots
  });
  const primaryRef = nodeContext.nodeIds[0] ?? "prompt";
  const stem = taskStem(clock.now(), primaryRef);
  const path23 = await uniqueMarkdownPath(fs21, dir, stem);
  input.onPathAllocated?.(path23);
  const now = clock.now();
  const requester = resolveDispatchRequester({ requester: input.requester });
  const acceptMode = input.acceptMode ?? DEFAULT_ACCEPT_MODE;
  if (!isAcceptMode(acceptMode)) {
    throw new Error(`Invalid Task acceptMode: ${String(acceptMode)}.`);
  }
  if (acceptMode !== "review-required" && !allowsNonReviewAcceptMode({
    requester
  })) {
    throw new Error(
      `acceptMode=${acceptMode} is only legal for a user-facing Task; downstream Task Agent \u2192 requester must use review-required (requester=${requester.kind}:${requester.id}).`
    );
  }
  const contextCard = buildTaskContextCard({
    ...nodeContext
  });
  const data = {
    type: "task",
    id,
    state: "queued",
    ...assigneeRoleId ? { assigneeRoleId } : {},
    ...executionSessionId ? { executionSessionId } : {},
    requester: serializeTaskActorRef(requester),
    contextCard: serializeTaskContextCardForFrontmatter(contextCard),
    manifest: input.manifestPath,
    acceptMode,
    createdAt: now,
    updatedAt: now
  };
  if (input.workspace) {
    data.workspace = input.workspace.workspace;
    data.worktree = input.workspace.worktree;
    data.branch = input.workspace.branch;
    data.targetBranch = input.workspace.targetBranch;
    const tip = typeof input.workspace.baseCommit === "string" ? input.workspace.baseCommit.trim() : "";
    if (tip) {
      data.baseCommit = tip;
      data.roleBranchBase = tip;
    }
  }
  const pointers = nodeContext.nodeSnapshots.map((snapshot) => `- ${snapshot.id}: ${snapshot.path}`).join("\n");
  const body = `# Task

## Context Pointers

${pointers || "(none)"}

- Manifest: ${input.manifestPath}
` + (input.id || id ? `- Task id: ${id}
` : "") + `
## Prompt

${prompt}
`;
  await fs21.writeFile(path23, serializeFrontmatter(data, body));
  return path23;
}
async function ackTaskRecord(fs21, path23) {
  await patchTaskRecord(fs21, path23, {
    state: "running"
  });
}
async function patchTaskRecord(fs21, path23, patch) {
  if ("acceptMode" in patch) {
    throw new Error("Task acceptMode is frozen at creation and cannot be patched.");
  }
  if ("contextCard" in patch) {
    throw new Error(
      "Task Context Card Node snapshots are frozen; patch contextGeneration only."
    );
  }
  if (!await fs21.exists(path23)) throw new Error(`Task record not found: ${path23}.`);
  const raw = await fs21.readFile(path23);
  const { data, body, keyOrder } = parseFrontmatter(raw);
  assertNoRetiredTaskAuthorityFields(data);
  if (data.type !== "task") throw new Error(`Invalid Task record format: ${path23}.`);
  if (patch.state) {
    data.state = patch.state;
  }
  if (typeof patch.executionSessionId === "string") {
    const executionSessionId2 = patch.executionSessionId.trim();
    if (!isSessionId(executionSessionId2)) throw new Error(`Invalid Task executionSessionId: ${executionSessionId2}.`);
    data.executionSessionId = executionSessionId2;
  }
  if (patch.wait === null) {
    delete data.waitReason;
    delete data.waitSummary;
    delete data.waitCode;
  } else if (patch.wait) {
    data.waitReason = patch.wait.reason;
    data.waitSummary = patch.wait.summary;
    const code = patch.wait.code?.trim();
    if (code) data.waitCode = code;
    else delete data.waitCode;
  }
  if (patch.currentResultId === null) delete data.currentResultId;
  else if (typeof patch.currentResultId === "string") {
    if (patch.currentResultId !== patch.currentResultId.trim() || !isTaskResultId(patch.currentResultId)) {
      throw new Error(`Invalid Task currentResultId: ${patch.currentResultId}.`);
    }
    data.currentResultId = patch.currentResultId;
  }
  if (patch.requester) {
    const nextRequester = parseTaskActorRef(patch.requester, "requester");
    data.requester = serializeTaskActorRef(nextRequester);
    const derived = deriveIntegrationAuthority({ requester: nextRequester });
    data.integrationAuthority = {
      actor: { kind: derived.actor.kind, id: derived.actor.id },
      mutator: "service"
    };
  }
  if (patch.statusDetail === null) delete data.statusDetail;
  else if (patch.statusDetail !== void 0) {
    data.statusDetail = parseTaskStatusDetail(patch.statusDetail);
  }
  if (patch.updatedAt) data.updatedAt = patch.updatedAt;
  for (const key2 of ["workspace", "worktree", "branch", "targetBranch"]) {
    const value = patch[key2];
    if (value === null) delete data[key2];
    else if (typeof value === "string") data[key2] = value;
  }
  if (patch.roleBranchBase === null) delete data.roleBranchBase;
  else if (typeof patch.roleBranchBase === "string" && patch.roleBranchBase.trim()) {
    data.roleBranchBase = patch.roleBranchBase.trim();
  }
  if (patch.baseCommit === null) delete data.baseCommit;
  else if (typeof patch.baseCommit === "string" && patch.baseCommit.trim()) {
    data.baseCommit = patch.baseCommit.trim();
  }
  if (patch.baseCommitCapture === null) delete data.baseCommitCapture;
  else if (patch.baseCommitCapture) {
    data.baseCommitCapture = serializeBaseCommitCapture(patch.baseCommitCapture);
  }
  if (patch.integrationAuthority === null) delete data.integrationAuthority;
  else if (patch.integrationAuthority) {
    if (data.requester === void 0 || data.requester === null) {
      throw new Error(
        "patchTaskRecord integrationAuthority requires requester on the Task record."
      );
    }
    const parentForAuth = parseTaskActorRef(data.requester, "requester");
    const validated = assertIntegrationAuthorityMatchesParent(
      patch.integrationAuthority,
      parentForAuth
    );
    data.integrationAuthority = {
      actor: { kind: validated.actor.kind, id: validated.actor.id },
      mutator: "service"
    };
  }
  if (patch.contextGeneration !== void 0) {
    if (!/^cg-v1-[a-f0-9]{64}$/.test(patch.contextGeneration)) {
      throw new Error("patchTaskRecord contextGeneration must be a canonical cg-v1 digest.");
    }
    const currentCard = loadTaskContextCardFromFrontmatter(data);
    if (!currentCard) {
      throw new Error(`Invalid Task record format: ${path23} (missing Task Context Card).`);
    }
    data.contextCard = serializeTaskContextCardForFrontmatter({
      ...currentCard,
      contextGeneration: patch.contextGeneration
    });
    delete data.contextGeneration;
  }
  const assigneeRoleId = typeof data.assigneeRoleId === "string" ? data.assigneeRoleId.trim() : "";
  const executionSessionId = typeof data.executionSessionId === "string" ? data.executionSessionId.trim() : "";
  if (!assigneeRoleId && !executionSessionId) {
    throw new Error("patchTaskRecord cannot remove the final assigneeRoleId/executionSessionId binding.");
  }
  if (assigneeRoleId && !isRoleId(assigneeRoleId)) throw new Error(`Invalid Task assigneeRoleId: ${assigneeRoleId}.`);
  if (executionSessionId && !isSessionId(executionSessionId)) throw new Error(`Invalid Task executionSessionId: ${executionSessionId}.`);
  await fs21.writeFile(path23, serializeFrontmatter(data, body, keyOrder));
  return loadTaskRecord(fs21, path23);
}
function parseTaskState(value) {
  if (value === "queued" || value === "running" || value === "waiting" || value === "submitted" || value === "accepted" || value === "rejected" || value === "interrupted" || value === "failed") {
    return value;
  }
  throw new Error(`Invalid task state: ${String(value)}.`);
}
function parseWaitFields(data) {
  const reason = data.waitReason;
  const summary = data.waitSummary;
  if ((reason === "user-input" || reason === "review" || reason === "external") && typeof summary === "string") {
    const code = typeof data.waitCode === "string" && data.waitCode.trim() ? data.waitCode.trim() : void 0;
    return { reason, summary, ...code ? { code } : {} };
  }
  return void 0;
}
function taskStem(now, claimId) {
  const stamp = now.replace(/[^0-9A-Za-z]+/g, "").slice(0, 14) || "task";
  return `task-${stamp}-${claimId.replace(/[^0-9A-Za-z_-]+/g, "-")}`;
}
async function uniqueMarkdownPath(fs21, dir, stem) {
  for (let n = 1; ; n++) {
    const suffix = n === 1 ? "" : `-${n}`;
    const path23 = join(dir, `${stem}${suffix}.md`);
    if (!await fs21.exists(path23)) return path23;
  }
}
async function ensureDir(fs21, path23) {
  if (!await fs21.exists(path23)) await fs21.mkdir(path23);
}

// src/core/scaffold.ts
function validateNodeName(value) {
  const name = value.trim();
  if (!name) throw new Error("Node name cannot be empty.");
  if (name.length > 200) throw new Error("Node name cannot be longer than 200 characters.");
  if (/[\/\\]/.test(name)) throw new Error("Node name cannot contain path separators.");
  if (/[\r\n]/.test(name)) throw new Error("Node name cannot contain newlines.");
  if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(name)) throw new Error("Node name cannot contain control characters.");
  return name;
}

// src/core/artifact.ts
import path from "node:path";
var ARTIFACT_KINDS = ["path", "directory", "commit", "url"];
var ArtifactRefError = class extends Error {
  constructor(message2) {
    super(message2);
    this.name = "ArtifactRefError";
  }
};
function isRecord4(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function normalizeWorkspaceTarget(value) {
  const target = value.trim();
  if (!target) throw new ArtifactRefError("Artifact path target must not be empty.");
  if (target.includes("\0")) throw new ArtifactRefError("Artifact path target must not contain NUL.");
  if (path.posix.isAbsolute(target) || path.win32.isAbsolute(target) || /^[a-zA-Z]:/.test(target) || target.startsWith("\\\\") || target.startsWith("//")) {
    throw new ArtifactRefError("Artifact path target must be workspace-relative.");
  }
  const portable = target.replace(/\\/g, "/");
  if (portable.split("/").some((segment) => segment === "..")) {
    throw new ArtifactRefError("Artifact path target must stay inside the workspace.");
  }
  const normalized = path.posix.normalize(portable).replace(/^\.\//, "").replace(/\/$/, "");
  if (!normalized || normalized === "." || normalized.startsWith("../")) {
    throw new ArtifactRefError("Artifact path target must name workspace content.");
  }
  return normalized;
}
function normalizeCommit(value) {
  const target = value.trim().toLowerCase();
  if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(target)) {
    throw new ArtifactRefError("Artifact commit target must be a full 40- or 64-character hex id.");
  }
  return target;
}
function normalizeUrl(value) {
  const target = value.trim();
  let parsed;
  try {
    parsed = new URL(target);
  } catch {
    throw new ArtifactRefError("Artifact URL target must be an absolute http(s) URL.");
  }
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
    throw new ArtifactRefError("Artifact URL target must use http or https.");
  }
  if (parsed.username || parsed.password) {
    throw new ArtifactRefError("Artifact URL target must not contain credentials.");
  }
  return parsed.href;
}
function normalizeArtifactRef(value) {
  if (!isRecord4(value)) throw new ArtifactRefError("Artifact ref must be an object.");
  const keys = Object.keys(value);
  if (keys.some((key2) => key2 !== "kind" && key2 !== "target" && key2 !== "label")) {
    throw new ArtifactRefError("Artifact ref contains unknown fields.");
  }
  if (typeof value.kind !== "string" || !ARTIFACT_KINDS.includes(value.kind)) {
    throw new ArtifactRefError(`Artifact kind must be one of: ${ARTIFACT_KINDS.join(", ")}.`);
  }
  if (typeof value.target !== "string") {
    throw new ArtifactRefError("Artifact target must be a string.");
  }
  if (value.label !== void 0 && typeof value.label !== "string") {
    throw new ArtifactRefError("Artifact label must be a string when present.");
  }
  const kind = value.kind;
  const target = kind === "path" || kind === "directory" ? normalizeWorkspaceTarget(value.target) : kind === "commit" ? normalizeCommit(value.target) : normalizeUrl(value.target);
  const label = value.label?.trim();
  return { kind, target, ...label ? { label } : {} };
}
function normalizeArtifactRefs(value) {
  if (!Array.isArray(value)) throw new ArtifactRefError("Artifact refs must be an array.");
  const byIdentity = /* @__PURE__ */ new Map();
  for (const item of value) {
    const ref = normalizeArtifactRef(item);
    const key2 = artifactRefIdentity(ref);
    const previous2 = byIdentity.get(key2);
    if (previous2 && previous2.label !== ref.label) {
      throw new ArtifactRefError(
        `Artifact ref has conflicting labels for the same target: ${ref.kind} ${ref.target}.`
      );
    }
    byIdentity.set(key2, ref);
  }
  return [...byIdentity.values()].sort((a, b) => {
    for (const [left, right] of [
      [a.kind, b.kind],
      [a.target, b.target]
    ]) {
      if (left < right) return -1;
      if (left > right) return 1;
    }
    return 0;
  });
}
function artifactRefIdentity(ref) {
  const normalized = normalizeArtifactRef(ref);
  return JSON.stringify([normalized.kind, normalized.target]);
}

// src/core/task-result.ts
var KEY_ORDER = [
  "type",
  "id",
  "taskId",
  "status",
  "commits",
  "targetHead",
  "checksJson",
  "artifactRefsJson",
  "integrationMode",
  "reviewer",
  "reviewAt",
  "reviewNote",
  "createdAt"
];
async function createTaskResultUnlocked(fs21, clock, input) {
  const record = buildTaskResultRecord(clock, input);
  const resultsDir = input.resultsDir.trim();
  if (!resultsDir) throw new Error("Task Result resultsDir cannot be empty.");
  await ensureDir2(fs21, resultsDir);
  if (await fs21.exists(record.path)) throw new Error(`Task Result already exists: ${record.path}.`);
  await writeTaskResult(fs21, record);
  return record;
}
function buildTaskResultRecord(clock, input) {
  const report = input.report.trim();
  if (!report) throw new Error("Task Result report cannot be empty.");
  if (!isCanonicalTaskId(input.taskId)) {
    throw new Error(`Task Result taskId must be canonical: ${input.taskId}.`);
  }
  if (input.id !== void 0 && !isCanonicalTaskResultId(input.id)) {
    throw new Error(`Task Result id must be canonical: ${input.id}.`);
  }
  const id = input.id ?? makeTaskResultId();
  const resultsDir = input.resultsDir.trim();
  if (!resultsDir) throw new Error("Task Result resultsDir cannot be empty.");
  const targetHead = normalizeTargetHead(input.targetHead);
  const createdAt = assertIsoTimestamp(clock.now(), "Task Result createdAt");
  const record = {
    path: join(resultsDir, `${id}.md`),
    id,
    taskId: input.taskId,
    status: input.status ?? "ready",
    report,
    commits: normalizeTaskResultCommits(input.commits ?? [], "Task Result commits"),
    ...targetHead ? { targetHead } : {},
    checks: normalizeChecks(input.checks ?? [], "Task Result checks"),
    artifactRefs: normalizeArtifactRefs(input.artifactRefs ?? []),
    integrationMode: input.integrationMode ?? null,
    createdAt
  };
  assertTaskResultRecord(record);
  return record;
}
async function loadTaskResult(fs21, inputPath) {
  const path23 = normalizeTaskResultPath(inputPath);
  if (!await fs21.exists(path23)) throw new Error(`Task Result not found: ${path23}.`);
  const raw = await fs21.readFile(path23);
  const identity = parseTaskResultIdentityFromRaw(raw);
  const { data, body } = parseFrontmatter(raw);
  if (!identity || data.type !== "task-result" || data.id !== identity.id || data.taskId !== identity.taskId || !path23.endsWith(`/${identity.id}.md`)) {
    throw new Error(`Invalid Task Result format: ${path23}.`);
  }
  if (typeof data.taskId !== "string" || !isCanonicalTaskId(data.taskId) || typeof data.createdAt !== "string") {
    throw new Error(`Invalid Task Result format: ${path23}.`);
  }
  const status = parseTaskResultStatus(data.status);
  const createdAt = assertIsoTimestamp(data.createdAt, "Task Result createdAt");
  const hasReviewer = Object.prototype.hasOwnProperty.call(data, "reviewer");
  const hasReviewAt = Object.prototype.hasOwnProperty.call(data, "reviewAt");
  const hasReviewNote = Object.prototype.hasOwnProperty.call(data, "reviewNote");
  const reviewer = typeof data.reviewer === "string" && data.reviewer.trim() ? data.reviewer.trim() : void 0;
  const reviewAt = typeof data.reviewAt === "string" ? assertIsoTimestamp(data.reviewAt, "Task Result reviewAt") : void 0;
  if (status === "ready" && (hasReviewer || hasReviewAt || hasReviewNote) || status !== "ready" && (!reviewer || !reviewAt) || hasReviewer !== hasReviewAt || hasReviewNote && typeof data.reviewNote !== "string") {
    throw new Error(`Invalid Task Result review projection: ${path23}.`);
  }
  const report = body.trim();
  if (!report) throw new Error(`Invalid Task Result report: ${path23}.`);
  const commits = parseCanonicalCommits(data.commits, path23);
  if (data.targetHead !== void 0 && typeof data.targetHead !== "string") {
    throw new Error(`Invalid Task Result targetHead: ${path23}.`);
  }
  const targetHead = normalizeTargetHead(data.targetHead);
  const record = {
    path: path23,
    id: data.id,
    taskId: data.taskId,
    status,
    report,
    commits,
    ...targetHead ? { targetHead } : {},
    checks: parseChecksField(data.checksJson, path23),
    artifactRefs: parseArtifactRefsField(data.artifactRefsJson, path23),
    integrationMode: parseIntegrationMode(data.integrationMode),
    review: reviewer && reviewAt ? {
      reviewer,
      at: reviewAt,
      note: typeof data.reviewNote === "string" ? data.reviewNote : void 0
    } : void 0,
    createdAt
  };
  assertTaskResultRecord(record);
  return record;
}
var TASK_RESULT_IDENTITY_PREFIX_MAX_BYTES = 1024;
function parseTaskResultIdentityFromRaw(raw) {
  const prefixBytes = new TextEncoder().encode(raw.slice(0, TASK_RESULT_IDENTITY_PREFIX_MAX_BYTES));
  const prefix = new TextDecoder("utf-8").decode(
    prefixBytes.subarray(0, TASK_RESULT_IDENTITY_PREFIX_MAX_BYTES)
  );
  if (!prefix.startsWith("---\n") && !prefix.startsWith("---\r\n")) return void 0;
  const values = /* @__PURE__ */ new Map();
  for (const line of prefix.split(/\r?\n/).slice(1)) {
    if (line === "---") break;
    const match = line.match(/^(type|id|taskId):\s*["']?([^"']+?)["']?\s*$/);
    if (!match) continue;
    if (values.has(match[1])) return void 0;
    values.set(match[1], match[2]);
  }
  const type = values.get("type");
  const id = values.get("id");
  const taskId = values.get("taskId");
  return type === "task-result" && id && taskId && isTaskResultId(id) && /^tk-[a-z0-9]+$/.test(taskId) ? { id, taskId } : void 0;
}
function taskResultReviewSemanticsDigest(record) {
  return canonicalSha256({
    version: 1,
    id: record.id,
    taskId: record.taskId,
    status: record.status,
    reportSha256: sha256Hex(record.report),
    commits: [...record.commits],
    targetHead: record.targetHead ?? null,
    checks: record.checks.map((check) => ({ ...check })),
    artifactRefs: normalizeArtifactRefs(record.artifactRefs),
    integrationMode: record.integrationMode,
    review: record.review ? { ...record.review } : null,
    createdAt: record.createdAt
  });
}
function taskResultAcceptCandidateDigest(record) {
  return canonicalSha256({
    version: 1,
    id: record.id,
    taskId: record.taskId,
    reportSha256: sha256Hex(record.report),
    commits: [...record.commits],
    targetHead: record.targetHead ?? null,
    checks: record.checks.map((check) => ({ ...check })),
    artifactRefs: normalizeArtifactRefs(record.artifactRefs),
    integrationMode: record.integrationMode,
    createdAt: record.createdAt
  });
}
async function loadTaskResults(fs21, filter) {
  const out = [];
  if (!await fs21.exists(TEMP_DIR)) return out;
  for (const entry of await fs21.listDir(TEMP_DIR)) {
    if (!entry.isDir || entry.name !== ROLES_TEMP_DIR && entry.name !== SESSIONS_TEMP_DIR) continue;
    const ownerRoot = join(TEMP_DIR, entry.name);
    for (const ownerEntry of await fs21.listDir(ownerRoot)) {
      if (!ownerEntry.isDir) continue;
      await collectTaskResultFiles(fs21, join(ownerRoot, ownerEntry.name, "results"), filter, out);
    }
  }
  return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b.id));
}
async function collectTaskResultFiles(fs21, dir, filter, out) {
  if (!await fs21.exists(dir)) return;
  for (const entry of await fs21.listDir(dir)) {
    if (entry.isDir || !/^rs-[a-z0-9]+\.md$/.test(entry.name)) continue;
    const d = await loadTaskResult(fs21, join(dir, entry.name));
    if (filter?.taskId && d.taskId !== filter.taskId) continue;
    out.push(d);
  }
}
async function writeTaskResult(fs21, record) {
  assertTaskResultRecord(record);
  if (!isCanonicalTaskResultId(record.id) || !record.path.endsWith(`/${record.id}.md`)) {
    throw new Error(`Task Result path must match its exact canonical id: ${record.path}.`);
  }
  const artifactRefs = normalizeArtifactRefs(record.artifactRefs);
  if (await fs21.exists(record.path)) {
    const current = await loadTaskResult(fs21, record.path);
    assertTaskResultWriteTransition(current, record);
  }
  const data = {
    type: "task-result",
    id: record.id,
    taskId: record.taskId,
    status: record.status,
    commits: record.commits,
    targetHead: record.targetHead,
    checksJson: record.checks.length ? JSON.stringify(record.checks) : void 0,
    artifactRefsJson: artifactRefs.length ? JSON.stringify(artifactRefs) : void 0,
    integrationMode: record.integrationMode,
    reviewer: record.review?.reviewer,
    reviewAt: record.review?.at,
    reviewNote: record.review?.note,
    createdAt: record.createdAt
  };
  await fs21.writeFile(record.path, serializeFrontmatter(data, record.report + "\n", KEY_ORDER));
}
function assertTaskResultWriteTransition(current, next) {
  if (taskResultAcceptCandidateDigest(current) !== taskResultAcceptCandidateDigest(next)) {
    throw new Error(`Task Result payload is immutable after creation: ${current.path}.`);
  }
  if (current.status === next.status) {
    if (taskResultReviewSemanticsDigest(current) !== taskResultReviewSemanticsDigest(next)) {
      throw new Error(`Task Result review projection cannot be rewritten: ${current.path}.`);
    }
    return;
  }
  if (current.status !== "ready" || next.status !== "accepted" && next.status !== "rejected") {
    throw new Error(
      `Task Result review may transition only once from ready to accepted or rejected: ${current.path}.`
    );
  }
}
function taskResultPathForTask(taskPath, resultId) {
  const normalizedTaskPath = taskPath.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
  if (!isTaskResultId(resultId)) {
    throw new Error(`Invalid Task Result id: ${resultId}.`);
  }
  const owner = /^(temp\/(?:roles|sessions)\/[^/]+)\/tasks\/[^/]+\.md$/.exec(
    normalizedTaskPath
  )?.[1];
  if (!owner) {
    throw new Error(`Task has no canonical Result namespace: ${taskPath}.`);
  }
  return `${owner}/results/${resultId}.md`;
}
function normalizeTaskResultPath(input) {
  const path23 = input.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
  if (!/^temp\/(roles|sessions)\/[^/]+\/results\/rs-[^/]+\.md$/.test(path23)) {
    throw new Error(
      "Task Result must point to temp/roles/<roleId>/results/<rs-id>.md or temp/sessions/<sessionId>/results/<rs-id>.md."
    );
  }
  return path23;
}
function parseTaskResultStatus(value) {
  if (value === "ready" || value === "accepted" || value === "rejected") return value;
  throw new Error(`Invalid Task Result status: ${String(value)}`);
}
function parseIntegrationMode(value) {
  if (value === void 0 || value === null || value === "null") return null;
  if (value === "auto-accept" || value === "agent-decided-integrate") {
    return value;
  }
  throw new Error(`Invalid Task Result integrationMode: ${String(value)}.`);
}
function parseChecksField(value, resultPath) {
  if (value === void 0 || value === null || value === "") return [];
  if (typeof value !== "string") {
    throw new Error(`Invalid Task Result checksJson: ${resultPath}.`);
  }
  try {
    return normalizeChecks(JSON.parse(value), `Task Result checks: ${resultPath}`);
  } catch {
    throw new Error(`Invalid Task Result checksJson: ${resultPath}.`);
  }
}
function normalizeChecks(value, label) {
  if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
  return value.map((item, index2) => {
    if (!item || typeof item !== "object" || Array.isArray(item)) {
      throw new Error(`${label}[${index2}] is invalid.`);
    }
    const o = item;
    if (Object.keys(o).some((key2) => key2 !== "name" && key2 !== "command" && key2 !== "exitCode") || typeof o.name !== "string" || !o.name.trim() || typeof o.command !== "string" || !o.command.trim() || typeof o.exitCode !== "number" || !Number.isInteger(o.exitCode)) {
      throw new Error(`${label}[${index2}] is invalid.`);
    }
    return { name: o.name.trim(), command: o.command.trim(), exitCode: o.exitCode };
  });
}
function parseArtifactRefsField(value, resultPath) {
  if (value === void 0 || value === null || value === "") return [];
  if (typeof value !== "string") {
    throw new Error(`Invalid Task Result artifactRefsJson: ${resultPath}.`);
  }
  let parsed;
  try {
    parsed = JSON.parse(value);
  } catch {
    throw new Error(`Invalid Task Result artifactRefsJson: ${resultPath}.`);
  }
  try {
    return normalizeArtifactRefs(parsed);
  } catch {
    throw new Error(`Invalid Task Result artifact refs: ${resultPath}.`);
  }
}
async function ensureDir2(fs21, path23) {
  if (!await fs21.exists(path23)) await fs21.mkdir(path23);
}
function uniqueCommits(commits) {
  return [...new Set(commits.map((c) => c.trim()).filter(Boolean))];
}
function normalizeTaskResultCommits(commits, label) {
  const normalized = uniqueCommits(commits);
  if (normalized.some((commit) => !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(commit))) {
    throw new Error(`${label} must contain canonical full 40- or 64-character Git object ids.`);
  }
  return normalized;
}
function parseCanonicalCommits(value, resultPath) {
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
    throw new Error(`Invalid Task Result commits: ${resultPath}.`);
  }
  const commits = normalizeTaskResultCommits(value, "Task Result commits");
  if (commits.length !== value.length || commits.some((commit, index2) => commit !== value[index2])) {
    throw new Error(`Non-canonical Task Result commits: ${resultPath}.`);
  }
  return commits;
}
function assertTaskResultRecord(record) {
  if (!isCanonicalTaskResultId(record.id) || !isCanonicalTaskId(record.taskId)) {
    throw new Error("Task Result identity must be canonical.");
  }
  if (!record.report.trim()) throw new Error("Task Result report cannot be empty.");
  assertIsoTimestamp(record.createdAt, "Task Result createdAt");
  parseTaskResultStatus(record.status);
  parseIntegrationMode(record.integrationMode);
  const commits = normalizeTaskResultCommits(record.commits, "Task Result commits");
  if (commits.length !== record.commits.length || commits.some((commit, index2) => commit !== record.commits[index2])) {
    throw new Error("Task Result commits must be canonical.");
  }
  if (commits.length > 0 && !record.targetHead) {
    throw new Error("Commit-bearing Task Result requires a canonical targetHead.");
  }
  if (commits.length === 0 && record.targetHead !== void 0) {
    throw new Error("Zero-commit Task Result cannot carry targetHead.");
  }
  if (record.targetHead !== void 0) normalizeTargetHead(record.targetHead);
  normalizeChecks(record.checks, "Task Result checks");
  normalizeArtifactRefs(record.artifactRefs);
  if (record.status === "ready") {
    if (record.review !== void 0) {
      throw new Error("Ready Task Result cannot carry a review projection.");
    }
    return;
  }
  if (!record.review?.reviewer.trim()) {
    throw new Error(`${record.status} Task Result requires a reviewer.`);
  }
  assertIsoTimestamp(record.review.at, "Task Result reviewAt");
  if (record.review.note !== void 0 && typeof record.review.note !== "string") {
    throw new Error("Task Result review note must be a string.");
  }
}
function isCanonicalTaskResultId(value) {
  return isTaskResultId(value) && /^rs-[a-z0-9]+$/.test(value);
}
function isCanonicalTaskId(value) {
  return isTaskId(value) && /^tk-[a-z0-9]+$/.test(value);
}
function normalizeTargetHead(value) {
  if (value === void 0) return void 0;
  if (value !== value.trim() || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(value)) {
    throw new Error("Task Result targetHead must be a canonical full 40- or 64-character Git object id.");
  }
  return value;
}

// src/core/task-lifecycle.ts
var DEFAULT_TASK_REJECT_NOTE = "Rejected; waiting for resubmission.";
async function taskClaim(env, taskPath, options = {}) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    const requestedSessionId = options.claimWrite?.executionSessionId;
    if (task.executionSessionId && requestedSessionId && requestedSessionId !== task.executionSessionId) {
      throw new Error(
        `Cannot claim task with a different Session: bound=${task.executionSessionId} requested=${requestedSessionId}`
      );
    }
    if (task.state === "running") {
      if (requestedSessionId && requestedSessionId !== task.executionSessionId) {
        throw new Error(
          `Cannot claim task with a different Session: bound=${task.executionSessionId ?? "missing"} requested=${requestedSessionId}`
        );
      }
      return task;
    }
    assertTransition(task.state, "claim", "running");
    const now = env.clock.now();
    if (options.claimWrite) {
      if (options.claimWrite.executionSessionId && !isSessionId(options.claimWrite.executionSessionId)) {
        throw new Error(`task.claim requires a canonical Session id: ${options.claimWrite.executionSessionId}`);
      }
      return patchTaskRecord(env.fs, taskPath, {
        ...options.claimWrite,
        state: "running",
        updatedAt: options.claimWrite.updatedAt ?? now
      });
    }
    await ackTaskRecord(env.fs, taskPath);
    return loadTaskRecord(env.fs, taskPath);
  });
}
async function taskWait(env, taskPath, options) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    assertTransition(task.state, "wait", "waiting");
    const summary = options.summary.trim();
    if (!summary) throw new Error("task.wait requires a non-empty summary.");
    const code = options.code?.trim();
    const statusDetail = options.statusDetail ? parseTaskStatusDetail(options.statusDetail) : void 0;
    if (statusDetail?.executionSessionId && task.executionSessionId !== statusDetail.executionSessionId) {
      throw new TaskLifecycleError(
        "TASK_NOT_ACTIVE",
        `Task wait return Session mismatch: task=${task.executionSessionId ?? "unbound"} requested=${statusDetail.executionSessionId}.`
      );
    }
    return patchTaskRecord(env.fs, taskPath, {
      state: "waiting",
      wait: {
        reason: options.reason,
        summary,
        ...code ? { code } : {}
      },
      ...statusDetail ? { statusDetail } : {},
      updatedAt: env.clock.now()
    });
  });
}
async function taskResume(env, taskPath) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    if (task.state === "running" && !task.wait) return task;
    assertTransition(task.state, "resume", "running");
    return patchTaskRecord(env.fs, taskPath, {
      state: "running",
      wait: null,
      updatedAt: env.clock.now()
    });
  });
}
async function reconcileTaskLifecycle(env, taskPath) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    const rejectResume = await completedTaskRejectResume(env.fs, task);
    return {
      task,
      ...rejectResume ? { rejectResume } : {}
    };
  });
}
var TASK_SUBMIT_INTENT_TYPE = "task-result-submit-intent";
async function prepareTaskSubmit(env, taskPath, options) {
  return withMutation(env.fs, async () => {
    const pendingIntent = await loadTaskSubmitIntent(env.fs, taskPath);
    if (pendingIntent) {
      return recoverPendingTaskSubmit(env, taskPath, pendingIntent, options, true);
    }
    const task = await preflightTaskMutation(env, taskPath);
    const recovered = await recoverExistingTaskSubmit(env, taskPath, task, options);
    if (recovered) return recovered;
    assertSubmitPreconditions(task);
    await assertNoReadyResult(env.fs, task);
    const routing = resolveSubmitRouting(task.acceptMode, options.decision);
    const candidateInput = {
      taskId: requireCanonicalTaskId(task),
      report: options.report,
      commits: options.commits,
      targetHead: options.targetHead,
      checks: options.checks,
      artifactRefs: options.artifactRefs,
      status: "ready",
      integrationMode: routing.integrationMode,
      resultsDir: resultDirForTask(task)
    };
    const candidate = buildTaskResultRecord(env.clock, candidateInput);
    const intent = {
      type: TASK_SUBMIT_INTENT_TYPE,
      version: 1,
      taskId: candidate.taskId,
      resultId: candidate.id,
      candidateDigest: taskResultAcceptCandidateDigest(candidate),
      createdAt: candidate.createdAt
    };
    await writeTaskSubmitIntent(env.fs, taskPath, intent);
    const result = await createTaskResultUnlocked(
      env.fs,
      { now: () => intent.createdAt },
      { ...candidateInput, id: intent.resultId }
    );
    assertTaskSubmitIntentMatchesResult(intent, result);
    assertTransition(task.state, "submit", "submitted");
    const next = await patchTaskRecord(env.fs, taskPath, {
      state: "submitted",
      currentResultId: result.id,
      statusDetail: null,
      updatedAt: env.clock.now()
    });
    await env.fs.remove(taskSubmitIntentPath(taskPath));
    if (routing.autoIntegrate) {
      return {
        kind: "auto",
        resultId: result.id,
        commits: [...result.commits],
        ...result.targetHead ? { targetHead: result.targetHead } : {}
      };
    }
    return { kind: "done", result: { task: next, result, autoIntegrated: false } };
  });
}
async function recoverCommittedTaskResult(env, taskPath, expected) {
  return withMutation(env.fs, async () => {
    const submitIntent = await loadTaskSubmitIntent(env.fs, taskPath);
    if (submitIntent) await cleanupCommittedTaskSubmitIntent(env, taskPath, submitIntent);
    const task = await preflightTaskMutation(env, taskPath);
    const result = await findCommittedTaskResult(env.fs, task);
    if (!result || result.status !== "ready" && result.status !== "accepted") {
      return void 0;
    }
    assertCommittedResultMatchesTask(task, result);
    if (result.report !== expected.report.trim()) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Persisted TaskResult report or outcome does not match the managed report draft."
      );
    }
    const prepared = result.status === "accepted" ? {
      kind: "done",
      result: { task, result, autoIntegrated: isAutoIntegrationMode(result.integrationMode) }
    } : isAutoIntegrationMode(result.integrationMode) ? {
      kind: "auto",
      resultId: result.id,
      commits: [...result.commits],
      ...result.targetHead ? { targetHead: result.targetHead } : {}
    } : {
      kind: "done",
      result: { task, result, autoIntegrated: false }
    };
    const options = {
      report: result.report,
      commits: [...result.commits],
      checks: result.checks.map((check) => ({ ...check })),
      artifactRefs: result.artifactRefs.map((ref) => ({ ...ref })),
      ...result.targetHead ? { targetHead: result.targetHead } : {},
      ...result.integrationMode === "agent-decided-integrate" ? { decision: "integrate" } : {}
    };
    return { task, result, prepared, options };
  });
}
async function finalizeTaskSubmitAuto(env, taskPath, options, prepared) {
  return withMutation(env.fs, async () => {
    let task = await preflightTaskMutation(env, taskPath);
    const persisted = await requireTaskResultById(env.fs, task, prepared.resultId);
    if (task.currentResultId !== persisted.id) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result changed during auto-accept; refusing state write."
      );
    }
    if (persisted.status === "accepted") {
      if (!isAutoIntegrationMode(persisted.integrationMode)) {
        throw new TaskLifecycleError(
          "RESULT_CHANGED",
          "Accepted result is not an auto-integrate candidate; refusing recovery."
        );
      }
      if (task.state === "submitted") {
        task = await patchTaskRecord(env.fs, taskPath, {
          state: "accepted",
          currentResultId: persisted.id,
          statusDetail: null,
          wait: null,
          updatedAt: env.clock.now()
        });
      } else if (task.state !== "accepted") {
        throw new TaskLifecycleError(
          "INVALID_TRANSITION",
          `Cannot recover accepted TaskResult while Task is ${task.state}.`
        );
      }
      assertTaskSubmitCandidateMatches(task, persisted, options);
      assertPreparedResultMatches(persisted, prepared);
      return { task, result: persisted, autoIntegrated: true };
    }
    assertTransition(task.state, "accept", "accepted");
    const result = await requireActiveReadyResult(env.fs, task);
    if (result.id !== prepared.resultId) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result changed during auto-accept; refusing state write."
      );
    }
    if (!exactStringListEqual(result.commits, prepared.commits)) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result commits changed during auto-accept; refusing state write."
      );
    }
    if ((result.targetHead?.trim() || void 0) !== prepared.targetHead) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result targetHead changed during auto-accept; refusing state write."
      );
    }
    const routing = resolveSubmitRouting(task.acceptMode, options.decision);
    if (!routing.autoIntegrate) {
      throw new Error("Task acceptMode changed during integrate; refusing state write.");
    }
    assertTaskSubmitCandidateMatches(task, result, options);
    assertPreparedResultMatches(result, prepared);
    if (!task.requester) {
      throw new TaskLifecycleError("RESULT_CHANGED", "Task requester is missing during auto-accept.");
    }
    result.status = "accepted";
    const acceptedAt = env.clock.now();
    result.review = { reviewer: task.requester.id, at: acceptedAt };
    if (result.integrationMode !== routing.integrationMode) {
      throw new TaskLifecycleError("RESULT_CHANGED", "Task Result integration mode changed during auto-accept.");
    }
    await writeTaskResult(env.fs, result);
    const next = await patchTaskRecord(env.fs, taskPath, {
      state: "accepted",
      currentResultId: result.id,
      statusDetail: null,
      wait: null,
      updatedAt: acceptedAt
    });
    return { task: next, result, autoIntegrated: true };
  });
}
async function prepareTaskAccept(env, taskPath, options) {
  return withMutation(env.fs, async () => {
    const submitIntent = await loadTaskSubmitIntent(env.fs, taskPath);
    if (submitIntent) await cleanupCommittedTaskSubmitIntent(env, taskPath, submitIntent);
    const intents = await loadTaskReviewIntents(env.fs, taskPath);
    if (intents.accept) {
      const recovered = await reconcilePendingTaskAccept(env, taskPath, intents.accept);
      if (!recovered) throw new Error("Exact-Task accept recovery intent disappeared.");
      assertTaskAcceptRequestMatchesIntent(intents.accept, options);
      return {
        resultId: recovered.result.id,
        resultPath: recovered.result.path,
        // Git integration already preceded the committed accept intent.
        commits: [],
        recovered: recovered.acceptance,
        acceptIntent: intents.accept
      };
    }
    const task = await preflightTaskMutation(env, taskPath, {
      allowCommittedSubmitRecovery: true
    });
    assertTransition(task.state, "accept", "accepted");
    const result = await requireExpectedActiveReadyResult(
      env.fs,
      task,
      options.resultId
    );
    const actor = canonicalTaskAcceptActor(options.actor);
    assertReviewAuthority({
      actor,
      executorRoleId: task.assigneeRoleId,
      requester: task.requester,
      action: "accept"
    });
    return {
      resultId: result.id,
      resultPath: result.path,
      commits: [...result.commits],
      resultSemanticsDigest: taskResultReviewSemanticsDigest(result)
    };
  });
}
async function finalizeTaskAccept(env, taskPath, options, prepared) {
  return withMutation(env.fs, async () => {
    if (prepared.recovered) {
      if (!prepared.acceptIntent) {
        throw new Error("Recovered task.accept preparation is missing its exact intent.");
      }
      assertTaskAcceptRequestMatchesIntent(prepared.acceptIntent, options);
      return prepared.recovered;
    }
    const task = await preflightTaskMutation(env, taskPath, {
      allowCommittedSubmitRecovery: true
    });
    assertTransition(task.state, "accept", "accepted");
    const result = await requireExpectedActiveReadyResult(
      env.fs,
      task,
      options.resultId
    );
    if (result.id !== prepared.resultId) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result changed during integrate; refusing accept."
      );
    }
    if (!prepared.resultSemanticsDigest || taskResultReviewSemanticsDigest(result) !== prepared.resultSemanticsDigest) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result semantics changed during integrate; refusing accept."
      );
    }
    if (!exactStringListEqual(result.commits, prepared.commits)) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready result commits changed during accept; refusing accept."
      );
    }
    const actor = canonicalTaskAcceptActor(options.actor);
    assertReviewAuthority({
      actor,
      executorRoleId: task.assigneeRoleId,
      requester: task.requester,
      action: "accept"
    });
    const updatedAt = assertIsoTimestamp(env.clock.now(), "Task accept intent updatedAt");
    const intent = {
      type: TASK_ACCEPT_INTENT_TYPE,
      version: 1,
      taskId: requireCanonicalTaskId(task),
      resultId: result.id,
      resultPath: result.path,
      candidateDigest: taskResultAcceptCandidateDigest(result),
      actor,
      commits: [...result.commits],
      updatedAt
    };
    await writeTaskAcceptIntent(env.fs, taskPath, intent);
    result.status = "accepted";
    result.review = { reviewer: intent.actor, at: intent.updatedAt };
    await writeTaskResult(env.fs, result);
    const next = await patchTaskRecord(env.fs, taskPath, {
      state: "accepted",
      wait: null,
      updatedAt: intent.updatedAt
    });
    const accepted = { task: next, result };
    await env.fs.remove(taskAcceptIntentPath(taskPath));
    return accepted;
  });
}
function exactStringListEqual(current, prepared) {
  return current.length === prepared.length && current.every((value, index2) => value === prepared[index2]);
}
async function taskReject(env, taskPath, options) {
  return withMutation(env.fs, async () => {
    const submitIntent = await loadTaskSubmitIntent(env.fs, taskPath);
    if (submitIntent) await cleanupCommittedTaskSubmitIntent(env, taskPath, submitIntent);
    const intents = await loadTaskReviewIntents(env.fs, taskPath);
    if (intents.accept) {
      await reconcilePendingTaskAccept(env, taskPath, intents.accept);
    }
    const recovered = intents.reject ? await reconcilePendingTaskReject(env, taskPath) : void 0;
    if (recovered) {
      assertTaskRejectRequestMatchesIntent(recovered.intent, options);
      const task2 = await preflightTaskMutation(env, taskPath, {
        allowCommittedSubmitRecovery: true
      });
      return { task: task2, result: recovered.result };
    }
    let task = await loadTaskRecord(env.fs, taskPath);
    task = await reconcileCommittedTaskResult(env, taskPath, task);
    const completed = await recoverCompletedTaskReject(env.fs, task, options);
    if (completed) return completed;
    const resume = options.resume !== false;
    const event = resume ? "reject-resume" : "reject-terminal";
    const to = resume ? "running" : "rejected";
    assertTransition(task.state, event, to);
    const result = await requireExpectedActiveReadyResult(
      env.fs,
      task,
      options.resultId
    );
    assertReviewAuthority({
      actor: options.actor,
      executorRoleId: task.assigneeRoleId,
      requester: task.requester,
      action: "reject"
    });
    const intent = {
      type: TASK_REJECT_INTENT_TYPE,
      version: 1,
      taskId: requireCanonicalTaskId(task),
      resultId: result.id,
      to,
      actor: options.actor,
      note: options.note?.trim() || DEFAULT_TASK_REJECT_NOTE,
      updatedAt: env.clock.now()
    };
    await writeTaskRejectIntent(env.fs, taskPath, intent);
    return completeTaskRejectIntent(env, taskPath, task, result, intent);
  });
}
async function recoverCompletedTaskReject(fs21, task, options) {
  if (!task.currentResultId || task.state !== "running" && task.state !== "rejected") {
    return void 0;
  }
  const result = await requireTaskResultById(fs21, task, task.currentResultId);
  if (result.status !== "rejected" || !result.review) {
    return void 0;
  }
  const completedIntent = {
    type: TASK_REJECT_INTENT_TYPE,
    version: 1,
    taskId: requireCanonicalTaskId(task),
    resultId: result.id,
    to: task.state,
    actor: result.review.reviewer,
    note: result.review.note || DEFAULT_TASK_REJECT_NOTE,
    updatedAt: result.review.at
  };
  assertTaskRejectRequestMatchesIntent(completedIntent, options);
  return { task, result };
}
async function completedTaskRejectResume(fs21, task) {
  if (task.state !== "running" || !task.currentResultId) return void 0;
  const result = await requireTaskResultById(fs21, task, task.currentResultId);
  if (result.status !== "rejected") return void 0;
  if (!isReadyTaskResultModeForTask(task, result.integrationMode)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Rejected TaskResult ${result.id} integration mode does not match its exact Task.`
    );
  }
  if (!result.review?.reviewer?.trim() || typeof result.review.note !== "string") {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Rejected TaskResult ${result.id} does not contain a complete review continuation.`
    );
  }
  return {
    resultId: result.id,
    actor: result.review.reviewer,
    note: result.review.note
  };
}
async function taskInterrupt(env, taskPath) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    if (task.state === "interrupted") {
      return task;
    }
    if (task.state === "queued") {
      assertTransition(task.state, "interrupt", "interrupted");
      await env.fs.remove(taskPath);
      return { ...task, state: "interrupted" };
    }
    assertTransition(task.state, "interrupt", "interrupted");
    return patchTaskRecord(env.fs, taskPath, {
      state: "interrupted",
      wait: null,
      updatedAt: env.clock.now()
    });
  });
}
async function taskRecordFailedReturn(env, taskPath, options) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    if (task.state !== "running" && task.state !== "waiting") return task;
    const executionSessionId = assertTaskFailureSession(task, options.executionSessionId);
    const statusDetail = parseTaskStatusDetail({
      kind: "failed",
      ...options.report ? { report: options.report } : {},
      error: options.error,
      ...options.code ? { code: options.code } : {},
      at: env.clock.now(),
      ...executionSessionId ? { executionSessionId } : {}
    });
    return patchTaskRecord(env.fs, taskPath, {
      statusDetail,
      updatedAt: env.clock.now()
    });
  });
}
async function taskFail(env, taskPath, options = {}) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    if (task.state === "failed") {
      const requestedStatusDetail = buildTaskFailedReturn(task, options, env.clock.now());
      if (task.statusDetail?.kind === "failed") return task;
      return patchTaskRecord(env.fs, taskPath, {
        statusDetail: requestedStatusDetail,
        updatedAt: env.clock.now()
      });
    }
    assertTransition(task.state, "fail", "failed");
    const statusDetail = buildTaskFailedReturn(task, options, env.clock.now());
    return patchTaskRecord(env.fs, taskPath, {
      state: "failed",
      wait: null,
      statusDetail,
      updatedAt: env.clock.now()
    });
  });
}
function buildTaskFailedReturn(task, options, at) {
  const executionSessionId = assertTaskFailureSession(task, options.executionSessionId);
  return parseTaskStatusDetail({
    kind: "failed",
    ...options.report ? { report: options.report } : {},
    error: options.error?.trim() || options.summary?.trim() || "Task failed.",
    ...options.code ? { code: options.code } : {},
    at,
    ...executionSessionId ? { executionSessionId } : {}
  });
}
function assertTaskFailureSession(task, requestedSessionId) {
  const requested = requestedSessionId?.trim() || void 0;
  if (requested && requested !== task.executionSessionId) {
    throw new TaskLifecycleError(
      "TASK_NOT_ACTIVE",
      `Task failure Session mismatch: task=${task.executionSessionId ?? "unbound"} requested=${requested}.`
    );
  }
  return requested ?? task.executionSessionId;
}
async function taskCancel(env, taskPath) {
  return withMutation(env.fs, async () => {
    const task = await preflightTaskMutation(env, taskPath);
    assertTransition(task.state, "cancel", "interrupted");
    await env.fs.remove(taskPath);
  });
}
function resultDirForTask(task) {
  const normalized = task.path.replace(/\\/g, "/").replace(/^\.\/+/, "");
  const match = /^(temp\/(?:roles|sessions)\/[^/]+)\/tasks\/[^/]+\.md$/.exec(normalized);
  if (!match) {
    throw new Error(
      `Task ${requireCanonicalTaskId(task)} has no canonical owner result namespace.`
    );
  }
  return `${match[1]}/results`;
}
function assertSubmitPreconditions(task) {
  if (task.state !== "running") {
    throw new TaskLifecycleError(
      "INVALID_TRANSITION",
      `task.submit requires state=running (got ${task.state}).`
    );
  }
}
async function assertNoReadyResult(fs21, task) {
  const existing = task.currentResultId ? await loadTaskResultById(fs21, task, task.currentResultId) : void 0;
  if (existing?.status === "ready") {
    throw new Error("A result is already ready for review; accept or reject it first.");
  }
}
async function requireActiveReadyResult(fs21, task) {
  if (!task.currentResultId) {
    throw new TaskLifecycleError("NO_ACTIVE_RESULT", "No ready result for this task.");
  }
  const result = await loadTaskResultById(fs21, task, task.currentResultId);
  if (!result || result.status !== "ready") {
    throw new TaskLifecycleError("NO_ACTIVE_RESULT", "No ready result for this task.");
  }
  return result;
}
async function requireExpectedActiveReadyResult(fs21, task, resultId) {
  if (task.currentResultId !== resultId) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "The ready result changed; refresh before reviewing it."
    );
  }
  const result = await requireActiveReadyResult(fs21, task);
  if (result.id !== resultId) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "The ready result changed; refresh before reviewing it."
    );
  }
  return result;
}
function taskSubmitIntentPath(taskPath) {
  if (!taskPath.endsWith(".md")) {
    throw new Error(`Task submit recovery requires a canonical markdown Task path: ${taskPath}.`);
  }
  return `${taskPath.slice(0, -3)}.result-submit-intent.json`;
}
async function writeTaskSubmitIntent(fs21, taskPath, intent) {
  const path23 = taskSubmitIntentPath(taskPath);
  if (await fs21.exists(path23)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task submit recovery intent already exists: ${path23}.`
    );
  }
  await fs21.writeFile(path23, JSON.stringify(intent, null, 2) + "\n");
}
async function loadTaskSubmitIntent(fs21, taskPath) {
  const path23 = taskSubmitIntentPath(taskPath);
  if (!await fs21.exists(path23)) return void 0;
  let raw;
  try {
    raw = JSON.parse(await fs21.readFile(path23));
  } catch {
    throw new Error(`Invalid exact-Task submit recovery intent: ${path23}.`);
  }
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    throw new Error(`Invalid exact-Task submit recovery intent: ${path23}.`);
  }
  const value = raw;
  const keys = Object.keys(value).sort();
  const expectedKeys = [
    "candidateDigest",
    "createdAt",
    "resultId",
    "taskId",
    "type",
    "version"
  ];
  let createdAt;
  try {
    createdAt = assertIsoTimestamp(String(value.createdAt ?? ""), "Task submit intent createdAt");
  } catch {
    throw new Error(`Invalid exact-Task submit recovery intent: ${path23}.`);
  }
  if (!exactStringListEqual(keys, expectedKeys) || value.type !== TASK_SUBMIT_INTENT_TYPE || value.version !== 1 || typeof value.taskId !== "string" || !isTaskId(value.taskId) || typeof value.resultId !== "string" || !isTaskResultId(value.resultId) || typeof value.candidateDigest !== "string" || !/^[a-f0-9]{64}$/.test(value.candidateDigest) || typeof value.createdAt !== "string" || value.createdAt !== createdAt) {
    throw new Error(`Invalid exact-Task submit recovery intent: ${path23}.`);
  }
  return value;
}
function taskSubmitCandidateForIntent(env, task, intent, options) {
  const routing = resolveSubmitRouting(task.acceptMode, options.decision);
  return buildTaskResultRecord(
    { now: () => intent.createdAt },
    {
      taskId: requireCanonicalTaskId(task),
      report: options.report,
      commits: options.commits,
      targetHead: options.targetHead,
      checks: options.checks,
      artifactRefs: options.artifactRefs,
      status: "ready",
      integrationMode: routing.integrationMode,
      resultsDir: resultDirForTask(task),
      id: intent.resultId
    }
  );
}
function assertTaskSubmitIntentMatchesResult(intent, result) {
  if (result.taskId !== intent.taskId || result.id !== intent.resultId || result.status !== "ready" || result.review !== void 0 || result.createdAt !== intent.createdAt || taskResultAcceptCandidateDigest(result) !== intent.candidateDigest) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task submit recovery Result ${intent.resultId} differs from its persisted intent.`
    );
  }
}
async function recoverPendingTaskSubmit(env, taskPath, intent, options, allowCreate) {
  const reviewIntents = await loadTaskReviewIntents(env.fs, taskPath);
  if (reviewIntents.accept || reviewIntents.reject) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Conflicting exact-Task submit and review recovery intents exist; refusing mutation."
    );
  }
  let task = await loadTaskRecord(env.fs, taskPath);
  if (requireCanonicalTaskId(task) !== intent.taskId) {
    throw new TaskLifecycleError("RESULT_CHANGED", "Task submit recovery intent identity changed.");
  }
  const candidate = taskSubmitCandidateForIntent(env, task, intent, options);
  if (taskResultAcceptCandidateDigest(candidate) !== intent.candidateDigest) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "This task.submit request differs from the persisted Result publication."
    );
  }
  const resultPath = candidate.path;
  let result;
  if (await env.fs.exists(resultPath)) {
    result = await loadTaskResult(env.fs, resultPath);
    assertTaskSubmitIntentMatchesResult(intent, result);
  } else {
    if (!allowCreate) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "A Task Result submit intent exists without its Result; retry the exact task.submit request."
      );
    }
    if (task.state !== "running" || task.currentResultId !== void 0) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Task state changed before its intended Result was published."
      );
    }
    result = await createTaskResultUnlocked(
      env.fs,
      { now: () => intent.createdAt },
      {
        taskId: candidate.taskId,
        report: candidate.report,
        commits: candidate.commits,
        targetHead: candidate.targetHead,
        checks: candidate.checks,
        artifactRefs: candidate.artifactRefs,
        status: "ready",
        integrationMode: candidate.integrationMode,
        resultsDir: resultDirForTask(task),
        id: intent.resultId
      }
    );
    assertTaskSubmitIntentMatchesResult(intent, result);
  }
  if (task.state === "running" && task.currentResultId === void 0) {
    task = await patchTaskRecord(env.fs, taskPath, {
      state: "submitted",
      currentResultId: result.id,
      statusDetail: null,
      updatedAt: env.clock.now()
    });
  } else if (task.state !== "submitted" || task.currentResultId !== result.id) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Task no longer matches its persisted Result publication."
    );
  }
  await env.fs.remove(taskSubmitIntentPath(taskPath));
  if (isAutoIntegrationMode(result.integrationMode)) {
    return {
      kind: "auto",
      resultId: result.id,
      commits: [...result.commits],
      ...result.targetHead ? { targetHead: result.targetHead } : {}
    };
  }
  return { kind: "done", result: { task, result, autoIntegrated: false } };
}
async function cleanupCommittedTaskSubmitIntent(env, taskPath, intent) {
  const reviewIntents = await loadTaskReviewIntents(env.fs, taskPath);
  if (reviewIntents.accept || reviewIntents.reject) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Conflicting exact-Task submit and review recovery intents exist; refusing mutation."
    );
  }
  const task = await loadTaskRecord(env.fs, taskPath);
  if (requireCanonicalTaskId(task) !== intent.taskId) {
    throw new TaskLifecycleError("RESULT_CHANGED", "Task submit recovery intent identity changed.");
  }
  const resultPath = taskResultPathForTask(taskPath, intent.resultId);
  if (!await env.fs.exists(resultPath)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "A Task Result publication is incomplete; retry the exact task.submit request."
    );
  }
  const result = await loadTaskResult(env.fs, resultPath);
  assertTaskSubmitIntentMatchesResult(intent, result);
  if (task.state !== "submitted" || task.currentResultId !== result.id) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "A Task Result publication is incomplete; retry the exact task.submit request."
    );
  }
  await env.fs.remove(taskSubmitIntentPath(taskPath));
  return task;
}
var TASK_ACCEPT_INTENT_TYPE = "task-result-accept-intent";
function taskAcceptIntentPath(taskPath) {
  if (!taskPath.endsWith(".md")) {
    throw new Error(`Task accept recovery requires a canonical markdown Task path: ${taskPath}.`);
  }
  return `${taskPath.slice(0, -3)}.result-accept-intent.json`;
}
async function writeTaskAcceptIntent(fs21, taskPath, intent) {
  const path23 = taskAcceptIntentPath(taskPath);
  if (await fs21.exists(path23)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task accept recovery intent already exists: ${path23}.`
    );
  }
  await fs21.writeFile(path23, JSON.stringify(intent, null, 2) + "\n");
}
async function loadTaskAcceptIntent(fs21, taskPath) {
  const path23 = taskAcceptIntentPath(taskPath);
  if (!await fs21.exists(path23)) return void 0;
  let raw;
  try {
    raw = JSON.parse(await fs21.readFile(path23));
  } catch {
    throw new Error(`Invalid exact-Task accept recovery intent: ${path23}.`);
  }
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    throw new Error(`Invalid exact-Task accept recovery intent: ${path23}.`);
  }
  const value = raw;
  const keys = Object.keys(value).sort();
  const expectedKeys = [
    "actor",
    "candidateDigest",
    "commits",
    "resultId",
    "resultPath",
    "taskId",
    "type",
    "updatedAt",
    "version"
  ];
  let updatedAt;
  try {
    updatedAt = assertIsoTimestamp(String(value.updatedAt ?? ""), "Task accept intent updatedAt");
  } catch {
    throw new Error(`Invalid exact-Task accept recovery intent: ${path23}.`);
  }
  if (!exactStringListEqual(keys, expectedKeys) || value.type !== TASK_ACCEPT_INTENT_TYPE || value.version !== 1 || typeof value.taskId !== "string" || !isTaskId(value.taskId) || typeof value.resultId !== "string" || !isTaskResultId(value.resultId) || typeof value.resultPath !== "string" || !value.resultPath.trim() || value.resultPath !== value.resultPath.trim() || typeof value.candidateDigest !== "string" || !/^[a-f0-9]{64}$/.test(value.candidateDigest) || typeof value.actor !== "string" || !value.actor.trim() || value.actor !== value.actor.trim() || !Array.isArray(value.commits) || !value.commits.every(
    (item) => typeof item === "string" && Boolean(item.trim()) && item === item.trim()
  ) || new Set(value.commits).size !== value.commits.length || typeof value.updatedAt !== "string" || value.updatedAt !== updatedAt) {
    throw new Error(`Invalid exact-Task accept recovery intent: ${path23}.`);
  }
  return value;
}
async function loadTaskReviewIntents(fs21, taskPath) {
  const accept = await loadTaskAcceptIntent(fs21, taskPath);
  const reject = await loadTaskRejectIntent(fs21, taskPath);
  if (accept && reject) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Conflicting exact-Task accept and reject recovery intents exist; refusing mutation."
    );
  }
  return { ...accept ? { accept } : {}, ...reject ? { reject } : {} };
}
function assertTaskAcceptRequestMatchesIntent(intent, options) {
  if (options.resultId !== intent.resultId || canonicalTaskAcceptActor(options.actor) !== intent.actor) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "This task.accept request differs from the persisted accept operation that was recovered."
    );
  }
}
function canonicalTaskAcceptActor(actor) {
  return actor.trim();
}
async function reconcilePendingTaskAccept(env, taskPath, knownIntent) {
  const intent = knownIntent ?? await loadTaskAcceptIntent(env.fs, taskPath);
  if (!intent) return void 0;
  const acceptance = await completeTaskAcceptIntent(env, taskPath, intent);
  await env.fs.remove(taskAcceptIntentPath(taskPath));
  return {
    task: acceptance.task,
    result: acceptance.result,
    intent,
    acceptance
  };
}
async function completeTaskAcceptIntent(env, taskPath, intent) {
  const task = await loadTaskRecord(env.fs, taskPath);
  if (intent.taskId !== requireCanonicalTaskId(task) || task.currentResultId !== intent.resultId) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task accept recovery intent does not match Task ${requireCanonicalTaskId(task)}.`
    );
  }
  assertReviewAuthority({
    actor: intent.actor,
    executorRoleId: task.assigneeRoleId,
    requester: task.requester,
    action: "accept"
  });
  const result = await requireTaskResultById(env.fs, task, intent.resultId);
  assertTaskAcceptIntentCanConverge(task, result, intent);
  if (result.status === "ready") {
    result.status = "accepted";
    result.review = { reviewer: intent.actor, at: intent.updatedAt };
    await writeTaskResult(env.fs, result);
  }
  let next = task;
  if (task.state === "submitted") {
    next = await patchTaskRecord(env.fs, taskPath, {
      state: "accepted",
      wait: null,
      updatedAt: intent.updatedAt
    });
  }
  return { task: next, result };
}
function assertTaskAcceptIntentCanConverge(task, result, intent) {
  const taskStateMatches = result.status === "ready" ? task.state === "submitted" : result.status === "accepted" && (task.state === "submitted" || task.state === "accepted" && task.updatedAt === intent.updatedAt && task.wait == null);
  const resultMatches = result.status === "ready" ? isReadyTaskResultModeForTask(task, result.integrationMode) && !result.review : result.status === "accepted" && isReadyTaskResultModeForTask(task, result.integrationMode) && result.review?.at === intent.updatedAt && result.review.reviewer === intent.actor && result.review.note === void 0;
  if (result.path !== intent.resultPath || taskResultAcceptCandidateDigest(result) !== intent.candidateDigest || !exactStringListEqual(result.commits, intent.commits) || !taskStateMatches || !resultMatches) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task accept recovery cannot converge TaskResult ${result.id} from Task ${requireCanonicalTaskId(task)}.`
    );
  }
}
var TASK_REJECT_INTENT_TYPE = "task-result-reject-intent";
function taskRejectIntentPath(taskPath) {
  if (!taskPath.endsWith(".md")) {
    throw new Error(`Task reject recovery requires a canonical markdown Task path: ${taskPath}.`);
  }
  return `${taskPath.slice(0, -3)}.result-reject-intent.json`;
}
async function writeTaskRejectIntent(fs21, taskPath, intent) {
  await fs21.writeFile(taskRejectIntentPath(taskPath), JSON.stringify(intent, null, 2) + "\n");
}
async function loadTaskRejectIntent(fs21, taskPath) {
  const path23 = taskRejectIntentPath(taskPath);
  if (!await fs21.exists(path23)) return void 0;
  let raw;
  try {
    raw = JSON.parse(await fs21.readFile(path23));
  } catch {
    throw new Error(`Invalid exact-Task reject recovery intent: ${path23}.`);
  }
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    throw new Error(`Invalid exact-Task reject recovery intent: ${path23}.`);
  }
  const value = raw;
  if (value.type !== TASK_REJECT_INTENT_TYPE || value.version !== 1 || typeof value.taskId !== "string" || typeof value.resultId !== "string" || value.to !== "running" && value.to !== "rejected" || typeof value.actor !== "string" || !value.actor.trim() || typeof value.note !== "string" || !value.note.trim() || typeof value.updatedAt !== "string" || !value.updatedAt.trim()) {
    throw new Error(`Invalid exact-Task reject recovery intent: ${path23}.`);
  }
  return value;
}
async function reconcilePendingTaskReject(env, taskPath) {
  const intent = await loadTaskRejectIntent(env.fs, taskPath);
  if (!intent) return void 0;
  const task = await loadTaskRecord(env.fs, taskPath);
  if (intent.taskId !== requireCanonicalTaskId(task) || task.currentResultId !== intent.resultId) {
    throw new Error(`Exact-Task reject recovery intent does not match Task ${requireCanonicalTaskId(task)}.`);
  }
  assertReviewAuthority({
    actor: intent.actor,
    executorRoleId: task.assigneeRoleId,
    requester: task.requester,
    action: "reject"
  });
  const result = await requireTaskResultById(env.fs, task, intent.resultId);
  assertTaskRejectIntentCanConverge(task, result, intent);
  const completed = await completeTaskRejectIntent(env, taskPath, task, result, intent);
  return { ...completed, intent };
}
async function preflightTaskMutation(env, taskPath, options = {}) {
  const submitIntent = await loadTaskSubmitIntent(env.fs, taskPath);
  if (submitIntent) {
    if (!options.allowCommittedSubmitRecovery) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "A Task Result publication is incomplete; retry the exact task.submit request."
      );
    }
    return cleanupCommittedTaskSubmitIntent(env, taskPath, submitIntent);
  }
  const intents = await loadTaskReviewIntents(env.fs, taskPath);
  const accepted = intents.accept ? await reconcilePendingTaskAccept(env, taskPath, intents.accept) : void 0;
  const rejected = intents.reject ? await reconcilePendingTaskReject(env, taskPath) : void 0;
  const task = accepted?.task ?? rejected?.task ?? await loadTaskRecord(env.fs, taskPath);
  return reconcileCommittedTaskResult(env, taskPath, task);
}
function assertTaskRejectRequestMatchesIntent(intent, options) {
  const expectedTo = options.resume === false ? "rejected" : "running";
  const expectedNote = options.note?.trim() || DEFAULT_TASK_REJECT_NOTE;
  if (options.resultId !== intent.resultId || options.actor !== intent.actor || expectedTo !== intent.to || expectedNote !== intent.note) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "This task.reject request differs from the persisted reject operation that was recovered."
    );
  }
}
async function completeTaskRejectIntent(env, taskPath, task, result, intent) {
  if (result.status === "ready") {
    result.status = "rejected";
    result.review = {
      reviewer: intent.actor,
      at: intent.updatedAt,
      note: intent.note
    };
    await writeTaskResult(env.fs, result);
  } else if (result.status !== "rejected" || result.review?.reviewer !== intent.actor || result.review.at !== intent.updatedAt || result.review.note !== intent.note) {
    throw new Error(`Exact-Task reject recovery TaskResult ${result.id} does not match its intent.`);
  }
  let next = task;
  if (task.state === "submitted") {
    next = await patchTaskRecord(env.fs, taskPath, {
      state: intent.to,
      // Keep currentResultId for history; new submit checks ready-only.
      updatedAt: intent.updatedAt
    });
  } else if (task.state !== intent.to) {
    throw new Error(
      `Exact-Task reject recovery expected Task state submitted|${intent.to}, got ${task.state}.`
    );
  }
  await env.fs.remove(taskRejectIntentPath(taskPath));
  return { task: next, result };
}
function assertTaskRejectIntentCanConverge(task, result, intent) {
  if (task.state !== "submitted" && task.state !== intent.to) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task reject recovery cannot converge from Task state ${task.state}.`
    );
  }
  if (!isReadyTaskResultModeForTask(task, result.integrationMode)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Persisted reject TaskResult integration mode does not match its exact Task."
    );
  }
  if (result.status === "ready") {
    if (result.review) {
      throw new TaskLifecycleError(
        "RESULT_CHANGED",
        "Ready TaskResult already carries review authority; refusing reject recovery."
      );
    }
    return;
  }
  if (result.status !== "rejected" || result.review?.reviewer !== intent.actor || result.review.at !== intent.updatedAt || result.review.note !== intent.note) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `Exact-Task reject recovery TaskResult ${result.id} does not match its intent.`
    );
  }
}
async function recoverExistingTaskSubmit(env, taskPath, task, options) {
  const result = await findCommittedTaskResult(env.fs, task);
  if (!result || result.status !== "ready" && result.status !== "accepted") return void 0;
  assertCommittedResultMatchesTask(task, result);
  assertTaskSubmitCandidateMatches(task, result, options);
  let next = task;
  if (result.status === "ready") {
    if (task.state === "running") {
      next = await patchTaskRecord(env.fs, taskPath, {
        state: "submitted",
        currentResultId: result.id,
        statusDetail: null,
        updatedAt: env.clock.now()
      });
    } else if (task.state !== "submitted" || task.currentResultId !== result.id) {
      return void 0;
    }
  } else {
    if (!isAutoIntegrationMode(result.integrationMode)) return void 0;
    if (task.state === "submitted" && task.currentResultId === result.id) {
      next = await patchTaskRecord(env.fs, taskPath, {
        state: "accepted",
        currentResultId: result.id,
        statusDetail: null,
        wait: null,
        updatedAt: env.clock.now()
      });
    } else if (task.state !== "accepted" || task.currentResultId !== result.id) {
      return void 0;
    }
  }
  if (result.status === "accepted") {
    return {
      kind: "done",
      result: { task: next, result, autoIntegrated: true }
    };
  }
  if (isAutoIntegrationMode(result.integrationMode)) {
    return {
      kind: "auto",
      resultId: result.id,
      commits: [...result.commits],
      ...result.targetHead ? { targetHead: result.targetHead } : {}
    };
  }
  return {
    kind: "done",
    result: { task: next, result, autoIntegrated: false }
  };
}
async function reconcileCommittedTaskResult(env, taskPath, task) {
  const result = await findCommittedTaskResult(env.fs, task);
  if (!result) return task;
  if (result.status === "ready" || result.status === "accepted") {
    assertCommittedResultMatchesTask(task, result);
  }
  if (result.status === "ready") {
    if (task.state === "submitted" && task.currentResultId === result.id) return task;
    throwCommittedTaskResultStateMismatch(task, result);
  }
  if (result.status === "accepted") {
    if (task.state === "submitted" && task.currentResultId === result.id) {
      return patchTaskRecord(env.fs, taskPath, {
        state: "accepted",
        currentResultId: result.id,
        statusDetail: null,
        wait: null,
        updatedAt: env.clock.now()
      });
    }
    if (task.state === "accepted" && task.currentResultId === result.id) return task;
    throwCommittedTaskResultStateMismatch(task, result);
  }
  return task;
}
function throwCommittedTaskResultStateMismatch(task, result) {
  throw new TaskLifecycleError(
    "RESULT_CHANGED",
    `Committed TaskResult ${result.id} (${result.status}) conflicts with Task ${requireCanonicalTaskId(task)} state ${task.state}; refusing mutation.`
  );
}
function assertCommittedResultMatchesTask(task, result) {
  const modeMatches = result.status === "ready" ? isReadyTaskResultModeForTask(task, result.integrationMode) : result.status === "accepted" ? isReadyTaskResultModeForTask(task, result.integrationMode) : true;
  if (!modeMatches) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Persisted TaskResult integration mode does not match its exact Task; refusing recovery."
    );
  }
}
function isReadyTaskResultModeForTask(task, integrationMode) {
  return task.acceptMode === "review-required" && integrationMode === null || task.acceptMode === "auto-accept" && integrationMode === "auto-accept" || task.acceptMode === "agent-decide" && (integrationMode === null || integrationMode === "agent-decided-integrate");
}
function assertTaskSubmitCandidateMatches(task, result, options) {
  const routing = resolveSubmitRouting(task.acceptMode, options.decision);
  const expectedCommits = [...new Set((options.commits ?? []).map((value) => value.trim()).filter(Boolean))];
  const expectedTargetHead = options.targetHead?.trim() || void 0;
  const expectedChecks = options.checks ?? [];
  const expectedArtifacts = normalizeArtifactRefs(options.artifactRefs ?? []);
  if (result.report !== options.report.trim() || result.integrationMode !== routing.integrationMode || !exactStringListEqual(result.commits, expectedCommits) || (result.targetHead?.trim() || void 0) !== expectedTargetHead || !exactTaskResultChecksEqual(result.checks, expectedChecks) || !exactArtifactRefsEqual(result.artifactRefs, expectedArtifacts)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Persisted TaskResult candidate differs from this task.submit retry; refusing duplicate publication."
    );
  }
}
function exactTaskResultChecksEqual(current, expected) {
  return current.length === expected.length && current.every((check, index2) => {
    const candidate = expected[index2];
    return !!candidate && check.name === candidate.name && check.command === candidate.command && check.exitCode === candidate.exitCode;
  });
}
function exactArtifactRefsEqual(current, expected) {
  return current.length === expected.length && current.every((ref, index2) => {
    const candidate = expected[index2];
    return !!candidate && ref.kind === candidate.kind && ref.target === candidate.target && ref.label === candidate.label;
  });
}
function assertPreparedResultMatches(result, prepared) {
  if (result.id !== prepared.resultId || !exactStringListEqual(result.commits, prepared.commits) || (result.targetHead?.trim() || void 0) !== prepared.targetHead) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      "Ready result changed during auto-accept; refusing state write."
    );
  }
}
function isAutoIntegrationMode(mode) {
  return mode === "auto-accept" || mode === "agent-decided-integrate";
}
async function requireTaskResultById(fs21, task, resultId) {
  const result = await loadTaskResultById(fs21, task, resultId);
  if (!result) {
    throw new TaskLifecycleError("NO_ACTIVE_RESULT", "No result for this exact task and id.");
  }
  return result;
}
async function loadTaskResultById(fs21, task, resultId) {
  const path23 = join(resultDirForTask(task), `${resultId}.md`);
  if (!await fs21.exists(path23)) return void 0;
  const result = await loadTaskResult(fs21, path23);
  if (result.id !== resultId || result.taskId !== requireCanonicalTaskId(task)) {
    throw new TaskLifecycleError(
      "RESULT_CHANGED",
      `TaskResult ${resultId} does not belong to exact Task ${requireCanonicalTaskId(task)}.`
    );
  }
  return result;
}
async function findCommittedTaskResult(fs21, task) {
  if (!task.currentResultId) return void 0;
  const current = await loadTaskResultById(fs21, task, task.currentResultId);
  return current?.status === "ready" || current?.status === "accepted" ? current : void 0;
}
function requireCanonicalTaskId(task) {
  const id = task.id?.trim() || "";
  if (!isTaskId(id)) {
    throw new Error(`Task ${task.path} is missing its canonical tk-* id.`);
  }
  return id;
}
async function withMutation(fs21, action) {
  return withTentMutation(fs21, action);
}

// src/core/forkOps.ts
async function forkNode(env, nodeId) {
  return withTentMutation(env.fs, async () => forkNodeUnlocked(env, nodeId));
}
async function forkNodeUnlocked(env, nodeId) {
  const tent = await loadTent(env.fs);
  if (tent.duplicateIds.has(nodeId)) throw new Error(`Duplicate node id '${nodeId}' found; repair or fork the duplicate nodes before using this id.`);
  const source = tent.byId.get(nodeId);
  if (!source) throw new Error(`Node not found: ${nodeId}.`);
  if (!isUsableNode(source)) throw new Error("Invalid or archived nodes cannot be forked.");
  assertContentMutable(source, "forked");
  const parentPath = dirName(source.path);
  const forkPath = await uniqueSiblingPath(env.fs, parentPath, `${source.name} (fork)`);
  await copyTree(env.fs, source.path, forkPath);
  const sourceNodes = collectSubtree(source);
  const usedIds = new Set(tent.byId.keys());
  const idMap = /* @__PURE__ */ new Map();
  for (const node2 of sourceNodes) {
    const nextId = makeUniqueNodeId(usedIds, env.rand);
    usedIds.add(nextId);
    idMap.set(node2.id, nextId);
  }
  const forkRootId = idMap.get(source.id);
  for (const node2 of sourceNodes) {
    const rel = relativePath(source.path, node2.path);
    const nextPath = rel ? join(forkPath, rel) : forkPath;
    const notePath = nodeNotePath(nextPath);
    await ensureIdentityFileName(env.fs, nextPath, node2.path);
    const { data, body, keyOrder } = parseFrontmatter(await env.fs.readFile(notePath));
    data.id = idMap.get(node2.id);
    delete data.owner;
    delete data.status;
    delete data.forkOf;
    delete data.forkBase;
    await env.fs.writeFile(notePath, serializeFrontmatter(data, body, keyOrder));
  }
  const order = await loadOrder(env.fs);
  const parentKey = source.parent ? source.parent.id : ROOT_KEY;
  const siblings = (source.parent ? source.parent.children : tent.roots).map((node2) => node2.id);
  const idx = siblings.indexOf(source.id);
  siblings.splice(idx === -1 ? siblings.length : idx + 1, 0, forkRootId);
  order[parentKey] = siblings;
  for (const node2 of sourceNodes) {
    const oldChildren = order[node2.id];
    const newId = idMap.get(node2.id);
    if (oldChildren && newId) {
      order[newId] = oldChildren.map((id) => idMap.get(id)).filter((id) => !!id);
    }
  }
  await saveOrder(env.fs, order);
  return forkRootId;
}
async function uniqueSiblingPath(fs21, parentPath, base) {
  let n = 1;
  while (true) {
    const name = n === 1 ? base : `${base.replace(/\s\(fork\)$/, "")} (fork ${n})`;
    const candidate = join(parentPath, name);
    if (!await fs21.exists(candidate)) return candidate;
    n += 1;
  }
}
async function copyTree(fs21, from, to) {
  await fs21.mkdir(to);
  for (const entry of await fs21.listDir(from)) {
    const src = join(from, entry.name);
    const dst = join(to, entry.name);
    if (entry.isDir) await copyTree(fs21, src, dst);
    else await fs21.writeFile(dst, await fs21.readFile(src));
  }
}
function collectSubtree(node2, out = []) {
  out.push(node2);
  for (const child of node2.children) collectSubtree(child, out);
  return out;
}
function relativePath(root, child) {
  if (child === root) return "";
  return child.slice(root.length + 1);
}
async function ensureIdentityFileName(fs21, newNodePath, oldNodePath) {
  const expected = nodeNotePath(newNodePath);
  if (await fs21.exists(expected)) return;
  const oldName = `${baseName(oldNodePath)}.md`;
  const copied = join(newNodePath, oldName);
  if (await fs21.exists(copied)) await fs21.move(copied, expected);
}

// src/core/okf-index.ts
function buildNodeIndex(nodes) {
  const index2 = /* @__PURE__ */ new Map();
  for (const node2 of nodes) {
    const projected = toOkfNode(node2);
    addIndex(index2, projected.nodeId, projected);
    addIndex(index2, projected.id, projected);
    addIndex(index2, projected.path, projected);
    addIndex(index2, projected.notePath, projected);
    addIndex(index2, projected.name, projected);
  }
  return index2;
}
function resolveNode(index2, target) {
  const clean = target.trim().replace(/^\.\//, "").replace(/\.md$/i, "");
  const matches = index2.get(clean) ?? index2.get(`${clean}.md`) ?? index2.get(normalizeLookupKey(clean));
  if (matches?.length === 1) return matches[0];
  const normalized = normalizeLookupKey(clean);
  if (normalized.length >= 4) {
    const all2 = index2.get("__all__") ?? [];
    const fuzzy = all2.filter(
      (node2) => normalizeLookupKey(node2.name).includes(normalized)
    );
    if (fuzzy.length === 1) return fuzzy[0];
  }
  return matches?.length === 1 ? matches[0] : void 0;
}
function toOkfNode(node2) {
  const notePath = nodeNotePath(node2.path);
  return {
    id: notePath.replace(/\.md$/i, ""),
    nodeId: node2.id,
    path: node2.path,
    notePath,
    name: node2.name,
    ...node2.type ? { type: node2.type } : {}
  };
}
function addIndex(index2, key2, node2) {
  const clean = key2.trim();
  if (!clean) return;
  addRawIndex(index2, clean, node2);
  addRawIndex(index2, normalizeLookupKey(clean), node2);
  addRawIndex(index2, "__all__", node2);
}
function addRawIndex(index2, key2, node2) {
  if (!key2) return;
  const list2 = index2.get(key2) ?? [];
  if (!list2.some((item) => item.id === node2.id)) list2.push(node2);
  index2.set(key2, list2);
}
function normalizeLookupKey(value) {
  return value.toLowerCase().replace(/[\s、，,。:：;；/\\_\-.()[\]（）【】"'`]+/g, "");
}

// src/core/link-target.ts
function safePercentDecode(value) {
  try {
    if (!/%[0-9A-Fa-f]{2}/.test(value)) return value;
    return decodeURIComponent(value);
  } catch {
    return value;
  }
}
function normalizeTarget(raw, fromNotePath) {
  let t = raw.trim().replace(/\\/g, "/");
  if (t.startsWith("<") && t.endsWith(">")) t = t.slice(1, -1).trim();
  t = safePercentDecode(t);
  t = (t.split("#")[0]?.split("?")[0] ?? t).trim();
  if ((t.startsWith("./") || t.startsWith("../")) && fromNotePath) {
    const base = fromNotePath.replace(/\\/g, "/").split("/").slice(0, -1);
    for (const part of t.split("/")) {
      if (part === "." || part === "") continue;
      if (part === "..") base.pop();
      else base.push(part);
    }
    t = base.join("/");
  }
  return t.replace(/\.md$/i, "");
}

// src/core/renameOps.ts
async function renameNode(env, nodeIdOrPath, newNameRaw) {
  return withTentMutation(env.fs, async () => renameNodeUnlocked(env, nodeIdOrPath, newNameRaw));
}
async function renameNodeUnlocked(env, nodeIdOrPath, newNameRaw) {
  const newName = validateNodeName(newNameRaw);
  const tent = await loadTent(env.fs);
  const target = resolveRenameTarget(tent, nodeIdOrPath);
  if (!isUsableNode(target)) {
    throw new Error("Invalid or archived nodes cannot be renamed.");
  }
  assertContentMutable(target, "renamed");
  if (target.invalid || target.archived) {
    throw new Error("Invalid or archived nodes cannot be renamed.");
  }
  const oldPath = target.path;
  const oldName = target.name;
  if (newName === oldName) {
    return {
      id: target.id,
      oldPath,
      path: oldPath,
      name: oldName,
      pathMap: { [oldPath]: oldPath },
      rewrittenNotes: []
    };
  }
  const parentPath = dirName(oldPath);
  const newPath = join(parentPath, newName);
  assertNotOperationalPath(oldPath);
  assertNotOperationalPath(newPath);
  if (await env.fs.exists(newPath)) {
    throw new Error(`Rename target already exists: ${newPath}.`);
  }
  const siblings = target.parent ? target.parent.children : tent.roots;
  if (siblings.some((node2) => node2.id !== target.id && node2.name === newName)) {
    throw new Error(`A sibling Node already uses the name: ${newName}.`);
  }
  const subtree2 = collectSubtree2(target);
  const pathMap = /* @__PURE__ */ new Map();
  for (const node2 of subtree2) {
    const rel = relativePath2(oldPath, node2.path);
    const nextNodePath = rel ? join(newPath, rel) : newPath;
    pathMap.set(node2.path, nextNodePath);
    pathMap.set(
      nodeNotePath(node2.path).replace(/\.md$/i, ""),
      nodeNotePath(nextNodePath).replace(/\.md$/i, "")
    );
  }
  const conceptIndex = buildNodeIndex(tent.byPath.values());
  const rewriteOpts = {
    renameNodeId: target.id,
    conceptIndex
  };
  const plannedWrites = [];
  const rewrittenNotes = [];
  for (const node2 of tent.byPath.values()) {
    const notePath = nodeNotePath(node2.path);
    if (!await env.fs.exists(notePath)) continue;
    const raw = await env.fs.readFile(notePath);
    const { data, body, keyOrder } = parseFrontmatter(raw);
    if (typeof data.id === "string" && data.id !== node2.id) {
      throw new Error(`Refuse rename: frontmatter id drift on ${node2.path}.`);
    }
    const afterNodePath = pathMap.get(node2.path) ?? node2.path;
    const restyleFromNotePath = nodeNotePath(afterNodePath);
    const rewritten = rewriteNodeLinks(body, notePath, pathMap, oldName, newName, {
      ...rewriteOpts,
      restyleFromNotePath
    });
    if (!rewritten.changed) continue;
    plannedWrites.push({
      writePath: restyleFromNotePath,
      originalPath: notePath,
      originalContent: raw,
      newContent: serializeFrontmatter(data, rewritten.body, keyOrder)
    });
    rewrittenNotes.push(afterNodePath);
  }
  await env.fs.move(oldPath, newPath);
  let identityRenamed = false;
  const completedWrites = [];
  try {
    identityRenamed = await ensureIdentityFileName2(env.fs, newPath, oldName);
    for (const write of plannedWrites) {
      await env.fs.writeFile(write.writePath, write.newContent);
      completedWrites.push(write);
    }
  } catch (error) {
    await rollbackRename(env.fs, {
      oldPath,
      newPath,
      oldName,
      identityRenamed,
      completedWrites
    });
    throw error;
  }
  const pathMapRecord = {};
  for (const [from, to] of pathMap) pathMapRecord[from] = to;
  return {
    id: target.id,
    oldPath,
    path: newPath,
    name: newName,
    pathMap: pathMapRecord,
    rewrittenNotes: rewrittenNotes.sort()
  };
}
async function rollbackRename(fs21, args) {
  const { oldPath, newPath, oldName, identityRenamed, completedWrites } = args;
  const restoreErrors = [];
  for (let i = completedWrites.length - 1; i >= 0; i--) {
    const write = completedWrites[i];
    try {
      await fs21.writeFile(write.writePath, write.originalContent);
    } catch (err) {
      restoreErrors.push(
        `note ${write.writePath}: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
  try {
    const expectedNew = nodeNotePath(newPath);
    const legacyAfterMove = join(newPath, `${oldName}.md`);
    if ((identityRenamed || await fs21.exists(expectedNew)) && await fs21.exists(expectedNew) && !await fs21.exists(legacyAfterMove)) {
      await fs21.move(expectedNew, legacyAfterMove);
    }
  } catch (err) {
    restoreErrors.push(`identity: ${err instanceof Error ? err.message : String(err)}`);
  }
  try {
    if (await fs21.exists(newPath) && !await fs21.exists(oldPath)) {
      const expectedNew = nodeNotePath(newPath);
      const legacyAfterMove = join(newPath, `${oldName}.md`);
      if (!await fs21.exists(legacyAfterMove) && await fs21.exists(expectedNew)) {
        try {
          await fs21.move(expectedNew, legacyAfterMove);
        } catch {
        }
      }
      await fs21.move(newPath, oldPath);
    }
  } catch (err) {
    restoreErrors.push(`tree: ${err instanceof Error ? err.message : String(err)}`);
  }
  if (restoreErrors.length > 0) {
    throw new Error(
      `Rename failed after filesystem move, and rollback also failed: ${restoreErrors.join("; ")}`
    );
  }
}
function resolveRenameTarget(tent, nodeIdOrPath) {
  const key2 = nodeIdOrPath.trim().replace(/\\/g, "/");
  const byId = tent.byId.get(key2);
  if (byId) return byId;
  const byPath = tent.byPath.get(key2);
  if (byPath) return byPath;
  throw new Error(`Node not found: ${nodeIdOrPath}.`);
}
function assertNotOperationalPath(path23) {
  if (isOperationalPath(path23) || path23 === "temp" || path23.startsWith("temp/")) {
    throw new Error("temp/ and other system pipelines cannot be renamed as Nodes.");
  }
  const top = path23.split("/")[0] ?? "";
  if (top === "attachments" || top === ".tent") {
    throw new Error("System directories cannot be renamed as Nodes.");
  }
}
function collectSubtree2(node2, out = []) {
  out.push(node2);
  for (const child of node2.children) collectSubtree2(child, out);
  return out;
}
function relativePath2(root, child) {
  if (child === root) return "";
  return child.slice(root.length + 1);
}
async function ensureIdentityFileName2(fs21, newNodePath, oldName) {
  const expected = nodeNotePath(newNodePath);
  if (await fs21.exists(expected)) return false;
  const legacy = join(newNodePath, `${oldName}.md`);
  if (await fs21.exists(legacy)) {
    await fs21.move(legacy, expected);
    return true;
  }
  const entries = await fs21.listDir(newNodePath);
  const candidates = entries.filter((e) => !e.isDir && e.name.endsWith(".md") && e.name !== "index.md").map((e) => join(newNodePath, e.name));
  if (candidates.length === 1) {
    await fs21.move(candidates[0], expected);
    return true;
  }
  throw new Error(`Identity note missing after rename: expected ${expected}.`);
}
function rewriteNodeLinks(body, fromNotePath, pathMap, oldName, newName, opts) {
  if (pathMap.size === 0) return { body, changed: false };
  const oldPaths = [...pathMap.keys()].sort((a, b) => b.length - a.length);
  let next = body;
  let changed = false;
  next = next.replace(/\[([^\]]*)\]\((<[^>\n]+>|[^)\n]+)\)/g, (full, label, destRaw) => {
    const angled = destRaw.startsWith("<") && destRaw.endsWith(">");
    const inner = angled ? destRaw.slice(1, -1) : destRaw;
    const { url, titleTail } = splitMdUrlAndTitle(inner);
    if (!url || isExternalOrAnchor(url)) return full;
    const mapped = mapLinkTarget(url, fromNotePath, pathMap, oldPaths, oldName, newName, opts);
    if (!mapped) return full;
    changed = true;
    const dest = angled ? `<${mapped}${titleTail}>` : `${mapped}${titleTail}`;
    return `[${label}](${dest})`;
  });
  next = next.replace(
    /(^|[^!])\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g,
    (full, prefix, rawTarget, rawLabel) => {
      const target = rawTarget.trim();
      if (!target) return full;
      const { head, suffix } = splitWikiTarget(target);
      if (!head || isExternalOrAnchor(head)) return full;
      const nextHead = mapLinkTarget(head, fromNotePath, pathMap, oldPaths, oldName, newName, opts);
      if (!nextHead || nextHead === head) return full;
      changed = true;
      const labelPart = rawLabel !== void 0 ? `|${rawLabel}` : "";
      return `${prefix}[[${nextHead}${suffix}${labelPart}]]`;
    }
  );
  return { body: next, changed };
}
function mapLinkTarget(raw, fromNotePath, pathMap, oldPaths, oldName, newName, opts) {
  const { pathPart, tail } = splitDestTail(raw);
  if (!pathPart) return void 0;
  if (isUnqualifiedName(pathPart)) {
    return mapUnqualifiedName(pathPart, tail, oldName, newName, opts);
  }
  const restyleFrom = opts?.restyleFromNotePath ?? fromNotePath;
  const sourceMoved = restyleFrom.replace(/\\/g, "/") !== fromNotePath.replace(/\\/g, "/");
  const isRelativeForm = pathPart.startsWith("./") || pathPart.startsWith("../");
  const normalized = normalizeTarget(pathPart, fromNotePath);
  const mapped = resolveMappedPath(normalized, pathMap, oldPaths);
  if (!mapped && !(isRelativeForm && sourceMoved)) {
    return void 0;
  }
  const newAbs = mapped ?? normalized;
  const sourceHadMd = /\.md$/i.test(pathPart.split(/[?#]/)[0] ?? pathPart);
  const absTarget = sourceHadMd ? newAbs.endsWith(".md") ? newAbs : `${newAbs}.md` : newAbs.replace(/\.md$/i, "");
  const styled = restyleRelative(pathPart, restyleFrom, absTarget, sourceHadMd);
  if (styled === pathPart) return void 0;
  return styled + tail;
}
function mapUnqualifiedName(pathPart, tail, oldName, newName, opts) {
  if (!opts || oldName === newName) return void 0;
  const bare = pathPart.replace(/\.md$/i, "");
  const resolved = resolveNode(opts.conceptIndex, bare);
  if (!resolved || resolved.nodeId !== opts.renameNodeId) return void 0;
  const sourceHadMd = /\.md$/i.test(pathPart);
  const nextBare = bare === oldName || normalizeLookupLoose(bare) === normalizeLookupLoose(oldName) ? newName : newName;
  return (sourceHadMd ? `${nextBare}.md` : nextBare) + tail;
}
function resolveMappedPath(normalized, pathMap, oldPaths) {
  const clean = normalized.replace(/\\/g, "/").replace(/^\.\//, "");
  if (pathMap.has(clean)) return pathMap.get(clean);
  const noMd = clean.replace(/\.md$/i, "");
  if (pathMap.has(noMd)) return pathMap.get(noMd);
  for (const oldPath of oldPaths) {
    if (clean === oldPath || noMd === oldPath || clean === `${oldPath}.md`) {
      return pathMap.get(oldPath);
    }
  }
  return void 0;
}
function isUnqualifiedName(raw) {
  const t = raw.trim().replace(/\\/g, "/");
  if (!t || t.includes("/") || t.startsWith(".")) return false;
  return true;
}
function normalizeLookupLoose(value) {
  return value.toLowerCase().replace(/[\s、，,。:：;；/\\_\-.()[\]（）【】"'`]+/g, "");
}
function splitMdUrlAndTitle(inner) {
  const t = inner.trim();
  const m = t.match(/^(\S+?)(\s+(".*"|'.*'|\(.*\)))\s*$/);
  if (m) return { url: m[1], titleTail: m[2] ?? "" };
  return { url: t, titleTail: "" };
}
function splitWikiTarget(raw) {
  const t = raw.trim();
  const caret = t.lastIndexOf("^");
  if (caret > 0) {
    const before = t.slice(0, caret);
    const hash2 = before.indexOf("#");
    if (hash2 >= 0) {
      return { head: before.slice(0, hash2).trim(), suffix: before.slice(hash2) + t.slice(caret) };
    }
    return { head: before.trim(), suffix: t.slice(caret) };
  }
  const hash = t.indexOf("#");
  if (hash >= 0) return { head: t.slice(0, hash).trim(), suffix: t.slice(hash) };
  return { head: t, suffix: "" };
}
function splitDestTail(dest) {
  const t = dest.trim();
  const hash = t.indexOf("#");
  const query = t.indexOf("?");
  let cut = -1;
  if (hash >= 0 && query >= 0) cut = Math.min(hash, query);
  else if (hash >= 0) cut = hash;
  else if (query >= 0) cut = query;
  if (cut < 0) return { pathPart: t, tail: "" };
  return { pathPart: t.slice(0, cut), tail: t.slice(cut) };
}
function isExternalOrAnchor(dest) {
  const t = dest.trim();
  if (!t || t.startsWith("#")) return true;
  return /^[a-z][a-z0-9+.-]*:/i.test(t);
}
function restyleRelative(originalPathPart, fromNotePath, absoluteNext, keepMd) {
  const orig = originalPathPart.replace(/\\/g, "/");
  if (orig.startsWith("./") || orig.startsWith("../")) {
    const toNote = absoluteNext.endsWith(".md") ? absoluteNext : `${absoluteNext}.md`;
    let rel = relativeMarkdownPath(fromNotePath, toNote);
    if (!keepMd) rel = rel.replace(/\.md$/i, "");
    return rel;
  }
  if (!keepMd && absoluteNext.endsWith(".md")) return absoluteNext.replace(/\.md$/i, "");
  return absoluteNext;
}
function relativeMarkdownPath(fromNotePath, toNotePath) {
  const fromParts = dirName(fromNotePath).split("/").filter(Boolean);
  const toParts = toNotePath.split("/").filter(Boolean);
  while (fromParts.length > 0 && toParts.length > 0 && fromParts[0] === toParts[0]) {
    fromParts.shift();
    toParts.shift();
  }
  const up = fromParts.map(() => "..");
  const rel = [...up, ...toParts].join("/");
  return rel.startsWith(".") ? rel : `./${rel}`;
}

// src/core/moveOps.ts
async function moveNode(env, nodeId, newParentId, position2) {
  return withTentMutation(
    env.fs,
    async () => moveNodeUnlocked(env, nodeId, newParentId, position2)
  );
}
async function moveNodeUnlocked(env, nodeId, newParentId, position2) {
  const id = nodeId.trim();
  if (!id) throw new Error("Node id is required for move.");
  const tent = await loadTent(env.fs);
  const moved = tent.byId.get(id);
  if (!moved) throw new Error(`Node not found: ${id}.`);
  if (!isUsableNode(moved)) throw new Error("Invalid or archived nodes cannot be moved.");
  assertContentMutable(moved, "moved");
  if (moved.invalid || moved.archived) {
    throw new Error("Invalid or archived nodes cannot be moved.");
  }
  assertNotOperationalPath2(moved.path);
  const parentNode = resolveNewParent(tent, newParentId);
  if (parentNode) {
    if (!isUsableNode(parentNode)) throw new Error("Target parent node is invalid or archived.");
    assertContentMutable(parentNode, "used as move parent");
    assertNotOperationalPath2(parentNode.path);
  }
  const newParentPath = parentNode ? parentNode.path : "";
  if (newParentPath === moved.path || newParentPath.startsWith(moved.path + "/")) {
    throw new Error("Cannot move a node into its own subtree.");
  }
  if (position2.mode !== "inside") {
    const sibling = tent.byId.get(position2.siblingId);
    if (!sibling) throw new Error(`Sibling not found: ${position2.siblingId}.`);
    const siblingParentId = sibling.parent ? sibling.parent.id : null;
    const destParentId = parentNode ? parentNode.id : null;
    if (siblingParentId !== destParentId) {
      throw new Error("before/after sibling must be under the destination parent.");
    }
    if (sibling.id === moved.id) {
      throw new Error("Cannot position a node relative to itself.");
    }
  }
  const oldPath = moved.path;
  const movedName = moved.name;
  const destination = join(newParentPath, movedName);
  const parentChanged = dirName(oldPath) !== newParentPath;
  if (parentChanged) {
    if (await env.fs.exists(destination)) {
      throw new Error(`Move target already exists: ${destination}.`);
    }
    const destSiblings = parentNode ? parentNode.children : tent.roots;
    if (destSiblings.some((node2) => node2.id !== moved.id && node2.name === movedName)) {
      throw new Error(`A sibling Node already uses the name: ${movedName}.`);
    }
  }
  const parentKey = parentNode ? parentNode.id : ROOT_KEY;
  const oldParentKey = moved.parent ? moved.parent.id : ROOT_KEY;
  const siblings = (parentNode ? parentNode.children : tent.roots).filter((b) => b.id !== moved.id).map((b) => b.id);
  let insertAt;
  if (position2.mode === "inside") {
    insertAt = siblings.length;
  } else {
    const idx = siblings.indexOf(position2.siblingId);
    insertAt = idx === -1 ? siblings.length : position2.mode === "before" ? idx : idx + 1;
  }
  siblings.splice(insertAt, 0, moved.id);
  if (!parentChanged) {
    const order = await loadOrder(env.fs);
    order[parentKey] = siblings;
    await saveOrder(env.fs, order);
    const identityMap = {};
    for (const node2 of collectSubtree3(moved)) {
      identityMap[node2.path] = node2.path;
      identityMap[nodeNotePath(node2.path).replace(/\.md$/i, "")] = nodeNotePath(node2.path).replace(
        /\.md$/i,
        ""
      );
    }
    return {
      id: moved.id,
      oldPath,
      path: oldPath,
      pathMap: identityMap,
      rewrittenNotes: []
    };
  }
  const subtree2 = collectSubtree3(moved);
  const pathMap = /* @__PURE__ */ new Map();
  for (const node2 of subtree2) {
    const rel = relativePath3(oldPath, node2.path);
    const nextNodePath = rel ? join(destination, rel) : destination;
    pathMap.set(node2.path, nextNodePath);
    pathMap.set(
      nodeNotePath(node2.path).replace(/\.md$/i, ""),
      nodeNotePath(nextNodePath).replace(/\.md$/i, "")
    );
  }
  const conceptIndex = buildNodeIndex(tent.byPath.values());
  const rewriteOpts = {
    renameNodeId: moved.id,
    conceptIndex
  };
  const plannedWrites = [];
  const rewrittenNotes = [];
  for (const node2 of tent.byPath.values()) {
    const notePath = nodeNotePath(node2.path);
    if (!await env.fs.exists(notePath)) continue;
    const raw = await env.fs.readFile(notePath);
    const { data, body, keyOrder } = parseFrontmatter(raw);
    if (typeof data.id === "string" && data.id !== node2.id) {
      throw new Error(`Refuse move: frontmatter id drift on ${node2.path}.`);
    }
    const afterNodePath = pathMap.get(node2.path) ?? node2.path;
    const restyleFromNotePath = nodeNotePath(afterNodePath);
    const rewritten = rewriteNodeLinks(body, notePath, pathMap, movedName, movedName, {
      ...rewriteOpts,
      restyleFromNotePath
    });
    if (!rewritten.changed) continue;
    plannedWrites.push({
      writePath: restyleFromNotePath,
      originalPath: notePath,
      originalContent: raw,
      newContent: serializeFrontmatter(data, rewritten.body, keyOrder)
    });
    rewrittenNotes.push(afterNodePath);
  }
  const orderBefore = await loadOrder(env.fs);
  const orderSnapshot = JSON.stringify(orderBefore);
  await env.fs.move(oldPath, destination);
  const completedWrites = [];
  try {
    for (const write of plannedWrites) {
      await env.fs.writeFile(write.writePath, write.newContent);
      completedWrites.push(write);
    }
    const order = JSON.parse(orderSnapshot);
    if (order[oldParentKey]) {
      order[oldParentKey] = order[oldParentKey].filter((sid) => sid !== moved.id);
    }
    order[parentKey] = siblings;
    await saveOrder(env.fs, order);
  } catch (error) {
    await rollbackMove(env.fs, {
      oldPath,
      newPath: destination,
      completedWrites,
      orderSnapshot
    });
    throw error;
  }
  const pathMapRecord = {};
  for (const [from, to] of pathMap) pathMapRecord[from] = to;
  return {
    id: moved.id,
    oldPath,
    path: destination,
    pathMap: pathMapRecord,
    rewrittenNotes: rewrittenNotes.sort()
  };
}
async function rollbackMove(fs21, args) {
  const { oldPath, newPath, completedWrites, orderSnapshot } = args;
  const restoreErrors = [];
  for (let i = completedWrites.length - 1; i >= 0; i--) {
    const write = completedWrites[i];
    try {
      await fs21.writeFile(write.writePath, write.originalContent);
    } catch (err) {
      restoreErrors.push(
        `note ${write.writePath}: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
  try {
    if (await fs21.exists(newPath) && !await fs21.exists(oldPath)) {
      await fs21.move(newPath, oldPath);
    }
  } catch (err) {
    restoreErrors.push(`tree: ${err instanceof Error ? err.message : String(err)}`);
  }
  try {
    await saveOrder(fs21, JSON.parse(orderSnapshot));
  } catch (err) {
    restoreErrors.push(`order: ${err instanceof Error ? err.message : String(err)}`);
  }
  if (restoreErrors.length > 0) {
    throw new Error(
      `Move failed after filesystem move, and rollback also failed: ${restoreErrors.join("; ")}`
    );
  }
}
function resolveNewParent(tent, newParentId) {
  if (newParentId === null || newParentId === void 0 || newParentId === "") {
    return null;
  }
  const parent = tent.byId.get(newParentId.trim());
  if (!parent) throw new Error(`Target parent not found: ${newParentId}.`);
  return parent;
}
function assertNotOperationalPath2(path23) {
  if (isOperationalPath(path23) || path23 === "temp" || path23.startsWith("temp/")) {
    throw new Error("temp/ and other system pipelines cannot be moved as Nodes.");
  }
  const top = path23.split("/")[0] ?? "";
  if (top === "attachments" || top === ".tent") {
    throw new Error("System directories cannot be moved as Nodes.");
  }
}
function collectSubtree3(node2, out = []) {
  out.push(node2);
  for (const child of node2.children) collectSubtree3(child, out);
  return out;
}
function relativePath3(root, child) {
  if (child === root) return "";
  return child.slice(root.length + 1);
}

// src/core/ops.ts
async function dispatch(env, options) {
  if ("resultPolicy" in options) {
    throw new Error("task.dispatch contains retired resultPolicy; use acceptMode.");
  }
  return withMutation2(env.fs, async () => dispatchUnlocked(env, options));
}
async function dispatchUnlocked(env, options) {
  const tent = await loadTent(env.fs);
  const prompt = options.prompt?.trim() || "";
  if (!prompt) throw new Error("Dispatch requires a user prompt.");
  const roleId = options.assigneeRoleId?.trim() || "";
  const sessionId = options.executionSessionId?.trim() || "";
  if (!roleId && !sessionId) throw new Error("Dispatch requires assigneeRoleId or executionSessionId.");
  if (roleId && !isRoleId(roleId)) throw new Error(`Invalid Role id: ${roleId}.`);
  if (sessionId && !isSessionId(sessionId)) throw new Error(`Invalid Session id: ${sessionId}.`);
  const selection = normalizeTaskNodeSelection({ nodeIds: options.nodeIds });
  const nodeIds = orderedTaskNodeIds(selection);
  const tasks = await loadTaskRecords(env.fs);
  const createdRoot = roleId ? roleTempRoot(roleId) : sessionTempRoot(sessionId);
  const createdRootExisted = await env.fs.exists(createdRoot);
  const requestedTaskId = options.taskId?.trim() || "";
  if (requestedTaskId && !isTaskId(requestedTaskId)) {
    throw new Error(`Invalid Task id: ${requestedTaskId}.`);
  }
  const taskId = requestedTaskId || makeTaskId();
  if (tasks.some((task) => task.id === taskId)) {
    throw new Error(`Task id already exists: ${taskId}.`);
  }
  const manifestPath = taskManifestPath(createdRoot, taskId);
  const initExpectedPath = roleId ? join(createdRoot, "init.md") : void 0;
  const manifestExisted = await env.fs.exists(manifestPath);
  const manifestBefore = manifestExisted ? await env.fs.readFile(manifestPath) : void 0;
  const initExisted = initExpectedPath ? await env.fs.exists(initExpectedPath) : false;
  const initBefore = initExisted && initExpectedPath ? await env.fs.readFile(initExpectedPath) : void 0;
  let allocatedTaskPath;
  if (!options.requester) {
    throw new Error(
      "Dispatch requires explicit requester."
    );
  }
  const selectedNodes = [];
  for (const id of nodeIds) {
    const node2 = requireNodeById(tent, id);
    if (node2.invalid) {
      throw new Error(`Cannot dispatch: Invalid subtree: ${node2.invalidReason || "missing type definition"}`);
    }
    selectedNodes.push(node2);
  }
  try {
    const input = {
      tentName: env.tentName,
      ...roleId ? { roleId } : {},
      ...sessionId ? { sessionId } : {},
      selectedNodes,
      ...options.workspace
    };
    const manifest = buildManifest(input);
    const yaml = manifestToYaml(manifest);
    let initPath;
    await ensureDir3(env.fs, dirName(manifestPath));
    await env.fs.writeFile(manifestPath, yaml);
    if (roleId) {
      const registry = await loadRolesRegistry(env.fs);
      const roleDefinition = registry.roles.find((item) => item.id === roleId);
      if (!roleDefinition) throw new Error(`Role not found in registry: ${roleId}.`);
      initPath = await ensureRoleInit(env.fs, roleDefinition, env.tentName);
    }
    const nodeSnapshots = [];
    const seenSnapshotIds = /* @__PURE__ */ new Set();
    const appendSnapshotSubtree = async (node2) => {
      if (seenSnapshotIds.has(node2.id)) return;
      seenSnapshotIds.add(node2.id);
      const raw = await env.fs.readFile(nodeNotePath(node2.path));
      nodeSnapshots.push(captureTaskNodeSnapshot(node2, contentEtag(raw)));
      for (const child of node2.children) {
        await appendSnapshotSubtree(child);
      }
    };
    for (const node2 of selectedNodes) {
      await appendSnapshotSubtree(node2);
    }
    const taskPath = await writeTaskRecord(env.fs, env.clock, {
      ...roleId ? { assigneeRoleId: roleId } : {},
      ...sessionId ? { executionSessionId: sessionId } : {},
      nodeIds: selection.nodeIds,
      nodeSnapshots,
      manifestPath,
      prompt,
      workspace: options.workspace,
      requester: options.requester,
      acceptMode: options.acceptMode,
      id: taskId,
      onPathAllocated: (path23) => {
        allocatedTaskPath = path23;
      }
    });
    const written = await loadTaskRecord(env.fs, taskPath);
    const relayPrompt = relayPromptForTask(written, env.tentRoot || env.tentName);
    return {
      manifestPath,
      manifestYaml: yaml,
      initPath,
      taskPath,
      relayPrompt,
      ...roleId ? { assigneeRoleId: roleId } : {},
      ...sessionId ? { executionSessionId: sessionId } : {}
    };
  } catch (error) {
    if (allocatedTaskPath && await env.fs.exists(allocatedTaskPath)) {
      await env.fs.remove(allocatedTaskPath);
    }
    if (manifestExisted && manifestBefore !== void 0) {
      await env.fs.writeFile(manifestPath, manifestBefore);
    } else if (await env.fs.exists(manifestPath)) {
      await env.fs.remove(manifestPath);
    }
    if (initExpectedPath) {
      if (initExisted && initBefore !== void 0) {
        await env.fs.writeFile(initExpectedPath, initBefore);
      } else if (await env.fs.exists(initExpectedPath)) {
        await env.fs.remove(initExpectedPath);
      }
    }
    if (!createdRootExisted && await env.fs.exists(createdRoot)) {
      const remaining = await env.fs.listDir(createdRoot);
      if (remaining.length === 0) await env.fs.remove(createdRoot);
    }
    throw error;
  }
}
async function createNode(env, input) {
  return withMutation2(env.fs, async () => createNodeUnlocked(env, input));
}
async function createNodeUnlocked(env, input) {
  assertNotTempPath(input.parentPath);
  const name = validateNodeName(input.name);
  const tent = await loadTent(env.fs);
  const type = normalizeOptionalNodeType(input.type);
  if (input.parentPath) {
    const parent2 = tent.byPath.get(input.parentPath);
    if (!parent2 || !isUsableNode(parent2)) throw new Error("Target parent node is invalid or archived.");
    assertContentMutable(parent2, "used as create parent");
  }
  const existing = new Set(tent.byId.keys());
  const id = makeUniqueNodeId(existing, env.rand);
  const path23 = join(input.parentPath, name);
  assertNotTempPath(path23);
  await ensureDir3(env.fs, path23);
  const fm = { id, ...type ? { type } : {} };
  const content3 = serializeFrontmatter(fm, `
# ${name}
`, NODE_FRONTMATTER_KEY_ORDER);
  await env.fs.writeFile(nodeNotePath(path23), content3);
  const parent = input.parentPath ? tent.byPath.get(input.parentPath) : void 0;
  const parentKey = parent ? parent.id : ROOT_KEY;
  try {
    const order = await loadOrder(env.fs);
    const siblings = order[parentKey] ?? [];
    order[parentKey] = siblings.includes(id) ? siblings : [...siblings, id];
    await saveOrder(env.fs, order);
  } catch (error) {
    await env.fs.remove(path23);
    throw error;
  }
  return id;
}
async function patchNode(env, nodePath8, patch, loadedTent) {
  await withMutation2(env.fs, async () => patchNodeUnlocked(env, nodePath8, patch, loadedTent));
}
async function patchNodeUnlocked(env, nodePath8, patch, loadedTent) {
  const tent = loadedTent ?? await loadTent(env.fs);
  const node2 = tent.byPath.get(nodePath8);
  if (!node2) throw new Error(`Node not found: ${nodePath8}.`);
  const reserved = [
    "id",
    "owner",
    "assignee",
    "mode",
    "archived",
    "readable",
    "writable",
    "status",
    "relations",
    // Output provenance is not a generic Node-editable field.
    "resultId"
  ].filter((key2) => key2 in patch);
  if (reserved.length > 0) {
    throw new Error(
      `Reserved or retired fields cannot be edited here: ${reserved.join(", ")}. Use docs.setMode for archive; collaboration status lives on Task projection; relations use relation.* RPCs; Output resultId is a separate Node-authority provenance concern.`
    );
  }
  if (node2.archived || node2.mode === "archived") {
    throw new Error("Archived nodes can only be restored or permanently deleted.");
  }
  if (node2.invalid) {
    const keys = Object.keys(patch);
    if (node2.id !== node2.invalidRootId || keys.some((key2) => key2 !== "type")) {
      throw new Error("Invalid subtrees can only be repaired by changing the type at the invalid root.");
    }
  }
  if ("type" in patch) patch = { ...patch, type: normalizeOptionalNodeType(patch.type) };
  const tagsTouched = "tags" in patch;
  const previousTags = node2.tags.slice();
  if (tagsTouched) {
    patch = { ...patch, tags: normalizeTagPatch(patch.tags) };
  }
  const nodeFile = nodeNotePath(nodePath8);
  const { data, body, keyOrder } = parseFrontmatter(await env.fs.readFile(nodeFile));
  for (const [k, v] of Object.entries(patch)) {
    if (v === void 0) delete data[k];
    else data[k] = v;
  }
  await env.fs.writeFile(nodeFile, serializeFrontmatter(data, body, nodeKeyOrder3(keyOrder)));
  if (tagsTouched) {
    const nextTags = Array.isArray(patch.tags) ? patch.tags : [];
    await syncTagRegistryAfterNodeTagsChangeUnlocked(env.fs, previousTags, nextTags);
  }
}
async function patchBody(env, nodePath8, newBody, loadedTent) {
  await withMutation2(env.fs, async () => patchBodyUnlocked(env, nodePath8, newBody, loadedTent));
}
async function patchBodyUnlocked(env, nodePath8, newBody, loadedTent) {
  const tent = loadedTent ?? await loadTent(env.fs);
  const node2 = tent.byPath.get(nodePath8);
  if (!node2) throw new Error(`Node not found: ${nodePath8}.`);
  if (!isUsableNode(node2)) throw new Error("Invalid or archived nodes cannot have their body edited.");
  assertContentMutable(node2, "body-edited");
  const nodeFile = nodeNotePath(nodePath8);
  const { data, keyOrder } = parseFrontmatter(await env.fs.readFile(nodeFile));
  await env.fs.writeFile(nodeFile, serializeFrontmatter(data, newBody, keyOrder));
}
async function setNodeMode(env, nodeId, mode) {
  await withMutation2(env.fs, async () => setNodeModeUnlocked(env, nodeId, mode));
}
async function setNodeModeUnlocked(env, nodeId, mode) {
  if (mode === "read-only") {
    throw new Error(
      'read-only mode is retired in V0.2; use "editable" or "archived" (archive freezes the subtree).'
    );
  }
  const next = parseNodeMode(mode);
  if (!next || next !== "editable" && next !== "archived") {
    throw new Error('mode must be "editable" or "archived".');
  }
  const tent = await loadTent(env.fs);
  const node2 = requireNodeById(tent, nodeId);
  if (node2.invalid) throw new Error("Invalid nodes cannot change mode.");
  if (node2.archived && !isExplicitArchiveRoot(node2)) {
    if (next === "archived") {
      throw new Error("Invalid or already archived nodes cannot be archived.");
    }
    throw new Error("Only an explicit archive root can leave archived mode; restore the archive root first.");
  }
  const current = isExplicitArchiveRoot(node2) ? "archived" : "editable";
  if (current === next) {
    if (next === "editable") {
      await patchFrontmatter(env.fs, node2, { mode: void 0, archived: void 0 });
    } else {
      await patchFrontmatter(env.fs, node2, { mode: "archived", archived: void 0 });
    }
    return;
  }
  if (next === "archived") {
    await patchFrontmatter(env.fs, node2, { mode: "archived", archived: void 0 });
    return;
  }
  await patchFrontmatter(env.fs, node2, { mode: void 0, archived: void 0 });
}
async function patchFrontmatter(fs21, node2, patch) {
  const nodeFile = nodeNotePath(node2.path);
  const { data, body, keyOrder } = parseFrontmatter(await fs21.readFile(nodeFile));
  for (const [k, v] of Object.entries(patch)) {
    if (v === void 0) delete data[k];
    else data[k] = v;
  }
  await fs21.writeFile(nodeFile, serializeFrontmatter(data, body, nodeKeyOrder3(keyOrder)));
}
async function ensureDir3(fs21, path23) {
  if (path23 && !await fs21.exists(path23)) await fs21.mkdir(path23);
}
function normalizeTagPatch(value) {
  if (value === void 0) return void 0;
  if (!Array.isArray(value)) throw new Error("Tags must be a string array.");
  const tags = [];
  for (const item of value) {
    if (typeof item !== "string") throw new Error("Tags must be a string array.");
    const tag = normalizeTagName(item);
    if (!tags.includes(tag)) tags.push(tag);
  }
  return tags.length > 0 ? tags.sort((a, b) => a.localeCompare(b)) : void 0;
}
function nodeKeyOrder3(existing) {
  return [
    ...NODE_FRONTMATTER_KEY_ORDER,
    ...existing.filter((key2) => !NODE_FRONTMATTER_KEY_ORDER.includes(key2))
  ];
}
function assertNotTempPath(path23) {
  if (path23 === "temp" || path23.startsWith("temp/")) {
    throw new Error("temp/ is a system pipeline; typed nodes cannot be created or moved there.");
  }
}
function requireNodeById(tent, nodeId) {
  if (tent.duplicateIds.has(nodeId)) {
    throw new Error(`Duplicate node id '${nodeId}' found; repair or fork the duplicate nodes before using this id.`);
  }
  const node2 = tent.byId.get(nodeId);
  if (!node2) throw new Error(`Node not found: ${nodeId}.`);
  return node2;
}
async function withMutation2(fs21, action) {
  return withTentMutation(fs21, action);
}

// src/core/managed-skill-compose.ts
import * as fs from "node:fs";
import * as path2 from "node:path";
var BUILTIN_TENT_TASK_SKILL = "tent-task";
var BUILTIN_TENT_ROLE_SKILL = "tent-role";
function builtinSkillNamesForExecutor(roleId) {
  if (roleId) {
    return [BUILTIN_TENT_ROLE_SKILL, BUILTIN_TENT_TASK_SKILL];
  }
  return [BUILTIN_TENT_TASK_SKILL];
}
function bundledSkillDir(packageRoot, skillName) {
  return path2.join(packageRoot, "skills", skillName);
}
function bundledSkillMdPath(packageRoot, skillName) {
  return path2.join(bundledSkillDir(packageRoot, skillName), "SKILL.md");
}
function readBundledSkillBody(packageRoot, skillName) {
  const file = bundledSkillMdPath(packageRoot, skillName);
  if (!fs.existsSync(file)) {
    throw new Error(`Built-in skill missing: ${skillName} (${file})`);
  }
  const raw = fs.readFileSync(file, "utf8");
  return stripYamlFrontmatter(raw).trim();
}
function readBundledSkillRaw(packageRoot, skillName) {
  const file = bundledSkillMdPath(packageRoot, skillName);
  if (!fs.existsSync(file)) {
    throw new Error(`Built-in skill missing: ${skillName} (${file})`);
  }
  return fs.readFileSync(file, "utf8");
}
function readBundledSkillVersion(packageRoot, skillName, packageVersion) {
  try {
    const raw = readBundledSkillRaw(packageRoot, skillName);
    const fromFm = parseSkillFrontmatterVersion(raw);
    if (fromFm) return fromFm;
  } catch {
  }
  const pkg = packageVersion?.trim();
  if (pkg) return pkg;
  return skillName;
}
function parseSkillFrontmatterVersion(raw) {
  const text3 = raw.replace(/^\uFEFF/, "");
  if (!text3.startsWith("---")) return void 0;
  const end = text3.indexOf("\n---", 3);
  if (end === -1) return void 0;
  const fm = text3.slice(3, end);
  for (const key2 of ["version", "compatibility", "schemaVersion"]) {
    const re = new RegExp(`^${key2}\\s*:\\s*["']?([^"'\\n#]+)`, "im");
    const m = fm.match(re);
    if (m?.[1]?.trim()) return m[1].trim();
  }
  return void 0;
}
function stripYamlFrontmatter(raw) {
  const text3 = raw.replace(/^\uFEFF/, "");
  if (!text3.startsWith("---")) return text3;
  const end = text3.indexOf("\n---", 3);
  if (end === -1) return text3;
  const after = text3.slice(end + 4);
  return after.replace(/^\r?\n/, "");
}
var STABLE_SKILL_CONTRACTS_END_MARKER = "--- End of stable Tent skill contracts ---";
function composeManagedSkillBootstrapPrefix(input) {
  const names = builtinSkillNamesForExecutor(input.roleId);
  const sections = [];
  if (names.includes(BUILTIN_TENT_ROLE_SKILL)) {
    const body = readBundledSkillBody(input.packageRoot, BUILTIN_TENT_ROLE_SKILL);
    sections.push(`## Built-in skill: ${BUILTIN_TENT_ROLE_SKILL}

${body}`);
    const role = input.role;
    const rolePrompt = role?.prompt?.trim() || "(no persistent role prompt)";
    sections.push(`## Role prompt

${rolePrompt}`);
  }
  if (names.includes(BUILTIN_TENT_TASK_SKILL)) {
    const body = readBundledSkillBody(input.packageRoot, BUILTIN_TENT_TASK_SKILL);
    sections.push(`## Built-in skill: ${BUILTIN_TENT_TASK_SKILL}

${body}`);
  }
  sections.push(STABLE_SKILL_CONTRACTS_END_MARKER);
  return sections.join("\n\n");
}
function composeManagedSkillRefs(input) {
  void input.packageRoot;
  const reserved = /* @__PURE__ */ new Set([
    BUILTIN_TENT_ROLE_SKILL.toLowerCase(),
    BUILTIN_TENT_TASK_SKILL.toLowerCase()
  ]);
  const out = [];
  const seen = new Set(reserved);
  for (const ref of input.connectionSkills ?? []) {
    if (!ref || ref.enabled === false) continue;
    const name = typeof ref.name === "string" ? ref.name.trim() : "";
    if (!name) continue;
    const key2 = name.toLowerCase();
    if (seen.has(key2)) continue;
    seen.add(key2);
    out.push({
      name,
      ...ref.path !== void 0 ? { path: ref.path } : {},
      enabled: true
    });
  }
  return out;
}
function managedSkillCompatibilityInputs(input) {
  const builtinSkills = builtinSkillNamesForExecutor(input.roleId);
  const skillBodyDigests = {};
  const skillVersions = {};
  const skillBodies = {};
  for (const name of builtinSkills) {
    const body = readBundledSkillBody(input.packageRoot, name);
    skillBodies[name] = body;
    skillBodyDigests[name] = simpleDigest(body);
    skillVersions[name] = readBundledSkillVersion(
      input.packageRoot,
      name,
      input.packageVersion
    );
  }
  return {
    builtinSkills,
    rolePrompt: input.role?.prompt?.trim() || "",
    skillBodyDigests,
    skillVersions,
    skillBodies
  };
}
function simpleDigest(text3) {
  let h = 2166136261 >>> 0;
  for (let i = 0; i < text3.length; i++) {
    h ^= text3.charCodeAt(i);
    h = Math.imul(h, 16777619) >>> 0;
  }
  return `d${h.toString(16).padStart(8, "0")}`;
}

// src/adapters/acp/mcp-skills.ts
import * as fs2 from "node:fs";
import * as os from "node:os";
import * as path3 from "node:path";
var SAFE_SKILL_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
var MCP_SERVER_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
var ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
var LAUNCH_SECRET_ID_RE = /^[a-z][a-z0-9-]{0,62}$/;
var MAX_ROUTE_SKILLS = 64;
var MAX_ROUTE_MCP_SERVERS = 32;
var MAX_MCP_ARGS = 64;
var MAX_MCP_ENV_ENTRIES = 32;
var MAX_MCP_HEADER_ENTRIES = 16;
function fieldOk(value) {
  return { ok: true, value };
}
function fieldErr(message2) {
  return { ok: false, message: message2 };
}
function defaultAllowedSkillRoots(home) {
  const root = home ?? os.homedir();
  return [
    path3.join(root, ".agents", "skills"),
    path3.join(root, ".claude", "skills"),
    path3.join(root, ".grok", "skills"),
    path3.join(root, ".cursor", "skills")
  ];
}
function normalizePathForCompare(p) {
  const resolved = path3.resolve(p);
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
}
function isPathInsideRoot(candidate, root) {
  const c = normalizePathForCompare(candidate);
  const r = normalizePathForCompare(root);
  if (c === r) return true;
  const rel = path3.relative(r, c);
  return rel !== "" && !rel.startsWith("..") && !path3.isAbsolute(rel);
}
function isUnderAllowedSkillRoots(absolutePath, allowedRoots) {
  const abs = path3.resolve(absolutePath);
  return allowedRoots.some((root) => isPathInsideRoot(abs, root));
}
function parseNonEmptyString(raw, key2) {
  if (typeof raw !== "string") {
    return fieldErr(`Invalid string param: ${key2}`);
  }
  const v = raw.trim();
  if (!v) {
    return fieldErr(`Invalid string param: ${key2} must be non-empty when set`);
  }
  return fieldOk(v);
}
function parseEnvKeyName(raw, key2) {
  const base = parseNonEmptyString(raw, key2);
  if (!base.ok) return base;
  if (!ENV_KEY_RE.test(base.value)) {
    return fieldErr(
      `Invalid ${key2}: must be a process env name (A-Za-z_ then A-Za-z0-9_)`
    );
  }
  return fieldOk(base.value);
}
function parseLaunchSecretId(raw, key2) {
  const base = parseNonEmptyString(raw, key2);
  if (!base.ok) return base;
  if (!LAUNCH_SECRET_ID_RE.test(base.value)) {
    return fieldErr(
      `Invalid ${key2}: must match ${LAUNCH_SECRET_ID_RE} (launch-secret id, not value)`
    );
  }
  return fieldOk(base.value);
}
function assertSafeSkillName(name) {
  const base = parseNonEmptyString(name, "skills[].name");
  if (!base.ok) return base;
  if (!SAFE_SKILL_NAME_RE.test(base.value) || base.value.includes("..")) {
    return fieldErr(
      `Invalid skills[].name: must match ${SAFE_SKILL_NAME_RE} (no path separators)`
    );
  }
  return fieldOk(base.value);
}
function parseStringRecord(raw, field, valueParse, maxEntries) {
  if (raw === void 0 || raw === null) return fieldOk(void 0);
  if (typeof raw !== "object" || Array.isArray(raw)) {
    return fieldErr(`Invalid ${field}: must be an object of string\u2192string`);
  }
  const entries = Object.entries(raw);
  if (entries.length > maxEntries) {
    return fieldErr(`Invalid ${field}: at most ${maxEntries} entries`);
  }
  const out = {};
  for (const [k, v] of entries) {
    const key2 = k.trim();
    if (!key2) return fieldErr(`Invalid ${field}: empty key`);
    const parsed = valueParse(v, `${field}.${key2}`);
    if (!parsed.ok) return parsed;
    out[key2] = parsed.value;
  }
  return fieldOk(out);
}
function parseEnvKeyMap(raw, field, maxEntries) {
  return parseStringRecord(raw, field, (v, k) => parseEnvKeyName(v, k), maxEntries);
}
function parseLaunchSecretRefMap(raw, field, maxEntries) {
  return parseStringRecord(raw, field, (v, k) => parseLaunchSecretId(v, k), maxEntries);
}
function parseSkillPathValue(raw, allowedRoots) {
  const base = parseNonEmptyString(raw, "skills[].path");
  if (!base.ok) return base;
  const p = base.value;
  if (!path3.isAbsolute(p)) {
    return fieldErr(
      "Invalid skills[].path: must be an absolute path under an allowed skill root"
    );
  }
  if (p.includes("\0")) {
    return fieldErr("Invalid skills[].path: contains NUL");
  }
  const resolved = path3.resolve(p);
  if (!isUnderAllowedSkillRoots(resolved, allowedRoots)) {
    return fieldErr(
      `Invalid skills[].path: must be under allowed skill roots (${allowedRoots.join(", ")})`
    );
  }
  return fieldOk(resolved);
}
function parseSkillRefValue(raw, allowedRoots) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    return fieldErr("Invalid skills[] entry: must be an object with name");
  }
  const o = raw;
  for (const key2 of Object.keys(o)) {
    if (key2 !== "name" && key2 !== "path" && key2 !== "enabled") {
      return fieldErr(`Unknown skills[] field: ${key2}`);
    }
  }
  const nameR = assertSafeSkillName(o.name);
  if (!nameR.ok) return nameR;
  let pathVal;
  if ("path" in o && o.path !== void 0 && o.path !== null) {
    const pr = parseSkillPathValue(o.path, allowedRoots);
    if (!pr.ok) return pr;
    pathVal = pr.value;
  }
  let enabled;
  if ("enabled" in o && o.enabled !== void 0 && o.enabled !== null) {
    if (typeof o.enabled !== "boolean") {
      return fieldErr("Invalid skills[].enabled: must be boolean");
    }
    enabled = o.enabled;
  }
  const ref = { name: nameR.value };
  if (pathVal !== void 0) ref.path = pathVal;
  if (enabled !== void 0) ref.enabled = enabled;
  return fieldOk(ref);
}
function parseSkillsArrayValue(raw, allowedRoots = defaultAllowedSkillRoots()) {
  if (raw === void 0 || raw === null) return fieldOk(void 0);
  if (!Array.isArray(raw)) {
    return fieldErr("Invalid skills: must be an array");
  }
  if (raw.length > MAX_ROUTE_SKILLS) {
    return fieldErr(`Invalid skills: at most ${MAX_ROUTE_SKILLS} entries`);
  }
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of raw) {
    const r = parseSkillRefValue(item, allowedRoots);
    if (!r.ok) return r;
    const key2 = r.value.name.toLowerCase();
    if (seen.has(key2)) {
      return fieldErr(`Duplicate skill name in skills: ${r.value.name}`);
    }
    seen.add(key2);
    out.push(r.value);
  }
  return fieldOk(out);
}
function parseMcpName(raw) {
  const base = parseNonEmptyString(raw, "mcpServers[].name");
  if (!base.ok) return base;
  if (!MCP_SERVER_NAME_RE.test(base.value)) {
    return fieldErr(
      `Invalid mcpServers[].name: must match ${MCP_SERVER_NAME_RE}`
    );
  }
  return fieldOk(base.value);
}
function parseHttpUrl(raw) {
  const base = parseNonEmptyString(raw, "mcpServers[].url");
  if (!base.ok) return base;
  let parsed;
  try {
    parsed = new URL(base.value);
  } catch {
    return fieldErr("Invalid mcpServers[].url: must be an absolute http(s) URL");
  }
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
    return fieldErr("Invalid mcpServers[].url: only http: and https: are allowed");
  }
  if (parsed.username || parsed.password) {
    return fieldErr(
      "Invalid mcpServers[].url: username/password in URL are not allowed"
    );
  }
  return fieldOk(base.value);
}
function parseMcpServerValue(raw) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    return fieldErr("Invalid mcpServers[] entry: must be an object");
  }
  const o = raw;
  const allowed = /* @__PURE__ */ new Set([
    "name",
    "transport",
    "enabled",
    "command",
    "args",
    "envKeys",
    "envSecretRefs",
    "url",
    "headerEnvKeys",
    "headerSecretRefs"
  ]);
  for (const key2 of Object.keys(o)) {
    if (!allowed.has(key2)) {
      const lower = key2.toLowerCase();
      if (lower === "env" || lower === "headers" || lower.includes("secret") || lower.includes("token") || lower.includes("apikey") || lower.includes("password") || lower.includes("authorization") || lower.includes("bearer")) {
        return fieldErr(
          `Rejected dangerous or unsupported mcpServers[] field: ${key2} (use envKeys/envSecretRefs/headerEnvKeys/headerSecretRefs)`
        );
      }
      return fieldErr(`Unknown mcpServers[] field: ${key2}`);
    }
  }
  const nameR = parseMcpName(o.name);
  if (!nameR.ok) return nameR;
  if (typeof o.transport !== "string" || o.transport !== "stdio" && o.transport !== "http") {
    return fieldErr("Invalid mcpServers[].transport: must be stdio|http");
  }
  const transport = o.transport;
  let enabled;
  if ("enabled" in o && o.enabled !== void 0 && o.enabled !== null) {
    if (typeof o.enabled !== "boolean") {
      return fieldErr("Invalid mcpServers[].enabled: must be boolean");
    }
    enabled = o.enabled;
  }
  const server = { name: nameR.value, transport };
  if (enabled !== void 0) server.enabled = enabled;
  if (transport === "stdio") {
    if ("url" in o && o.url !== void 0 && o.url !== null) {
      return fieldErr("Invalid mcpServers[]: stdio transport must not set url");
    }
    if ("headerEnvKeys" in o && o.headerEnvKeys != null) {
      return fieldErr("Invalid mcpServers[]: stdio transport must not set headerEnvKeys");
    }
    if ("headerSecretRefs" in o && o.headerSecretRefs != null) {
      return fieldErr(
        "Invalid mcpServers[]: stdio transport must not set headerSecretRefs"
      );
    }
    const cmd = parseNonEmptyString(o.command, "mcpServers[].command");
    if (!cmd.ok) return cmd;
    server.command = cmd.value;
    if ("args" in o && o.args !== void 0 && o.args !== null) {
      if (!Array.isArray(o.args) || !o.args.every((a) => typeof a === "string")) {
        return fieldErr("Invalid mcpServers[].args: must be an array of strings");
      }
      if (o.args.length > MAX_MCP_ARGS) {
        return fieldErr(`Invalid mcpServers[].args: at most ${MAX_MCP_ARGS} entries`);
      }
      server.args = o.args.map((a) => a);
    }
    const envKeys = parseEnvKeyMap(o.envKeys, "mcpServers[].envKeys", MAX_MCP_ENV_ENTRIES);
    if (!envKeys.ok) return envKeys;
    if (envKeys.value) server.envKeys = envKeys.value;
    const envCreds = parseLaunchSecretRefMap(
      o.envSecretRefs,
      "mcpServers[].envSecretRefs",
      MAX_MCP_ENV_ENTRIES
    );
    if (!envCreds.ok) return envCreds;
    if (envCreds.value) server.envSecretRefs = envCreds.value;
  } else {
    if ("command" in o && o.command !== void 0 && o.command !== null) {
      return fieldErr("Invalid mcpServers[]: http transport must not set command");
    }
    if ("args" in o && o.args !== void 0 && o.args !== null) {
      return fieldErr("Invalid mcpServers[]: http transport must not set args");
    }
    if ("envKeys" in o && o.envKeys != null) {
      return fieldErr("Invalid mcpServers[]: http transport must not set envKeys");
    }
    if ("envSecretRefs" in o && o.envSecretRefs != null) {
      return fieldErr(
        "Invalid mcpServers[]: http transport must not set envSecretRefs"
      );
    }
    const urlR = parseHttpUrl(o.url);
    if (!urlR.ok) return urlR;
    server.url = urlR.value;
    const headerEnv = parseEnvKeyMap(
      o.headerEnvKeys,
      "mcpServers[].headerEnvKeys",
      MAX_MCP_HEADER_ENTRIES
    );
    if (!headerEnv.ok) return headerEnv;
    if (headerEnv.value) server.headerEnvKeys = headerEnv.value;
    const headerCreds = parseLaunchSecretRefMap(
      o.headerSecretRefs,
      "mcpServers[].headerSecretRefs",
      MAX_MCP_HEADER_ENTRIES
    );
    if (!headerCreds.ok) return headerCreds;
    if (headerCreds.value) server.headerSecretRefs = headerCreds.value;
  }
  return fieldOk(server);
}
function parseMcpServersArrayValue(raw) {
  if (raw === void 0 || raw === null) return fieldOk(void 0);
  if (!Array.isArray(raw)) {
    return fieldErr("Invalid mcpServers: must be an array");
  }
  if (raw.length > MAX_ROUTE_MCP_SERVERS) {
    return fieldErr(`Invalid mcpServers: at most ${MAX_ROUTE_MCP_SERVERS} entries`);
  }
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of raw) {
    const r = parseMcpServerValue(item);
    if (!r.ok) return r;
    const key2 = r.value.name.toLowerCase();
    if (seen.has(key2)) {
      return fieldErr(`Duplicate mcpServers name: ${r.value.name}`);
    }
    seen.add(key2);
    out.push(r.value);
  }
  return fieldOk(out);
}
function cloneSkillRefs(skills) {
  if (!skills) return void 0;
  return skills.map((s) => ({
    name: s.name,
    ...s.path !== void 0 ? { path: s.path } : {},
    ...s.enabled !== void 0 ? { enabled: s.enabled } : {}
  }));
}
function cloneMcpServers(servers) {
  if (!servers) return void 0;
  return servers.map((s) => ({
    name: s.name,
    transport: s.transport,
    ...s.enabled !== void 0 ? { enabled: s.enabled } : {},
    ...s.command !== void 0 ? { command: s.command } : {},
    ...s.args !== void 0 ? { args: [...s.args] } : {},
    ...s.envKeys !== void 0 ? { envKeys: { ...s.envKeys } } : {},
    ...s.envSecretRefs !== void 0 ? { envSecretRefs: { ...s.envSecretRefs } } : {},
    ...s.url !== void 0 ? { url: s.url } : {},
    ...s.headerEnvKeys !== void 0 ? { headerEnvKeys: { ...s.headerEnvKeys } } : {},
    ...s.headerSecretRefs !== void 0 ? { headerSecretRefs: { ...s.headerSecretRefs } } : {}
  }));
}
function projectSkillRefs(skills) {
  if (!skills || skills.length === 0) return void 0;
  return skills.map((s) => ({
    name: s.name,
    ...s.path !== void 0 ? { path: s.path } : {},
    enabled: s.enabled !== false
  }));
}
function projectMcpServers(servers) {
  if (!servers || servers.length === 0) return void 0;
  return servers.map((s) => ({
    name: s.name,
    transport: s.transport,
    enabled: s.enabled !== false,
    ...s.command !== void 0 ? { command: s.command } : {},
    ...s.args !== void 0 ? { args: [...s.args] } : {},
    ...s.envKeys !== void 0 ? { envKeys: { ...s.envKeys } } : {},
    ...s.envSecretRefs !== void 0 ? { envSecretRefs: { ...s.envSecretRefs } } : {},
    ...s.url !== void 0 ? { url: s.url } : {},
    ...s.headerEnvKeys !== void 0 ? { headerEnvKeys: { ...s.headerEnvKeys } } : {},
    ...s.headerSecretRefs !== void 0 ? { headerSecretRefs: { ...s.headerSecretRefs } } : {}
  }));
}
function resolveEnvValue(envKey, planEnv) {
  const fromPlan = planEnv[envKey];
  if (typeof fromPlan === "string" && fromPlan.length > 0) return fromPlan;
  const fromProc = process.env[envKey];
  if (typeof fromProc === "string" && fromProc.length > 0) return fromProc;
  return void 0;
}
function resolveAcpMcpServersWire(servers, opts) {
  if (!servers || servers.length === 0) return [];
  const wire = [];
  for (const s of servers) {
    if (s.enabled === false) continue;
    if (s.transport === "stdio") {
      const command = s.command?.trim();
      if (!command) {
        throw new Error(`MCP server ${s.name}: stdio requires command`);
      }
      const env = [];
      if (s.envKeys) {
        for (const [envName, envKey] of Object.entries(s.envKeys)) {
          const value = resolveEnvValue(envKey, opts.planEnv);
          if (value === void 0) {
            throw new Error(
              `MCP server ${s.name}: missing process env ${envKey} for env var ${envName}`
            );
          }
          env.push({ name: envName, value });
        }
      }
      if (s.envSecretRefs) {
        for (const [envName, secretId] of Object.entries(s.envSecretRefs)) {
          if (!opts.resolveLaunchSecret) {
            throw new Error(
              `MCP server ${s.name}: launch secret ${secretId} requires resolveLaunchSecret hook`
            );
          }
          const value = opts.resolveLaunchSecret(secretId);
          if (typeof value !== "string" || !value) {
            throw new Error(
              `MCP server ${s.name}: launch secret not found or empty for ${secretId} (env ${envName})`
            );
          }
          env.push({ name: envName, value });
        }
      }
      wire.push({
        name: s.name,
        command,
        args: s.args ? [...s.args] : [],
        env
      });
    } else {
      const url = s.url?.trim();
      if (!url) {
        throw new Error(`MCP server ${s.name}: http requires url`);
      }
      const headers = [];
      if (s.headerEnvKeys) {
        for (const [headerName, envKey] of Object.entries(s.headerEnvKeys)) {
          const value = resolveEnvValue(envKey, opts.planEnv);
          if (value === void 0) {
            throw new Error(
              `MCP server ${s.name}: missing process env ${envKey} for header ${headerName}`
            );
          }
          headers.push({ name: headerName, value });
        }
      }
      if (s.headerSecretRefs) {
        for (const [headerName, secretId] of Object.entries(s.headerSecretRefs)) {
          if (!opts.resolveLaunchSecret) {
            throw new Error(
              `MCP server ${s.name}: launch secret ${secretId} requires resolveLaunchSecret hook`
            );
          }
          const value = opts.resolveLaunchSecret(secretId);
          if (typeof value !== "string" || !value) {
            throw new Error(
              `MCP server ${s.name}: launch secret not found or empty for ${secretId} (header ${headerName})`
            );
          }
          headers.push({ name: headerName, value });
        }
      }
      wire.push({
        name: s.name,
        type: "http",
        url,
        ...headers.length > 0 ? { headers } : {}
      });
    }
  }
  return wire;
}
function resolveAcpSkillMeta(skills, opts) {
  if (!skills || skills.length === 0) return [];
  const out = [];
  for (const s of skills) {
    if (s.enabled === false) continue;
    if (s.path && opts?.requirePathExists) {
      if (!fs2.existsSync(s.path)) {
        throw new Error(`Skill ${s.name}: path does not exist: ${s.path}`);
      }
    }
    out.push({
      name: s.name,
      ...s.path !== void 0 ? { path: s.path } : {}
    });
  }
  return out;
}

// src/runtime/agent-connection.ts
import { createHash as createHash3 } from "node:crypto";
function cloneAgentConnection(connection) {
  return {
    ...connection,
    args: connection.args ? [...connection.args] : void 0,
    skills: connection.skills?.length ? cloneSkillRefs(connection.skills) : void 0,
    mcpServers: connection.mcpServers?.length ? cloneMcpServers(connection.mcpServers) : void 0,
    fake: connection.fake ? { ...connection.fake } : void 0
  };
}
function stableJson(value) {
  if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
  if (!value || typeof value !== "object") return JSON.stringify(value);
  const record = value;
  return `{${Object.keys(record).sort().map((key2) => `${JSON.stringify(key2)}:${stableJson(record[key2])}`).join(",")}}`;
}
function calculateAgentConnectionLaunchDigest(connection, effectiveEndpointDigest) {
  const canonical = cloneAgentConnection(connection);
  const input = {
    connectionId: canonical.connectionId,
    provider: canonical.provider,
    adapterId: canonical.adapterId,
    command: canonical.command,
    args: canonical.args,
    model: canonical.model,
    envKey: canonical.envKey,
    launchSecretRef: canonical.launchSecretRef,
    endpoint: canonical.endpoint,
    effectiveEndpointDigest,
    permissionPolicy: canonical.permissionPolicy,
    promptTimeoutMs: canonical.promptTimeoutMs,
    permissionTimeoutMs: canonical.permissionTimeoutMs,
    skills: canonical.skills,
    mcpServers: canonical.mcpServers,
    fake: canonical.fake
  };
  return `sha256:${createHash3("sha256").update(stableJson(input)).digest("hex")}`;
}
function createAgentConnectionSnapshot(connection, details) {
  return {
    connectionId: connection.connectionId,
    provider: connection.provider,
    adapterId: connection.adapterId,
    ...connection.model ? { model: connection.model } : {},
    ...connection.command ? { command: connection.command } : {},
    ...connection.args ? { args: [...connection.args] } : {},
    ...connection.envKey ? { envKey: connection.envKey } : {},
    ...connection.launchSecretRef ? { launchSecretRef: connection.launchSecretRef } : {},
    ...connection.endpoint ? { endpoint: connection.endpoint } : {},
    ...connection.permissionPolicy ? { permissionPolicy: connection.permissionPolicy } : {},
    ...connection.promptTimeoutMs !== void 0 ? { promptTimeoutMs: connection.promptTimeoutMs } : {},
    ...connection.permissionTimeoutMs !== void 0 ? { permissionTimeoutMs: connection.permissionTimeoutMs } : {},
    ...connection.skills?.length ? { skills: cloneSkillRefs(connection.skills) } : {},
    ...connection.mcpServers?.length ? { mcpServers: cloneMcpServers(connection.mcpServers) } : {},
    ...connection.fake ? { fake: { ...connection.fake } } : {},
    ...details.effectiveEndpointDigest ? { effectiveEndpointDigest: details.effectiveEndpointDigest } : {},
    launchDigest: calculateAgentConnectionLaunchDigest(connection, details.effectiveEndpointDigest)
  };
}
function connectionConfigFromSnapshot(snapshot) {
  return {
    connectionId: snapshot.connectionId,
    provider: snapshot.provider,
    adapterId: snapshot.adapterId,
    ...snapshot.model ? { model: snapshot.model } : {},
    ...snapshot.command ? { command: snapshot.command } : {},
    ...snapshot.args ? { args: [...snapshot.args] } : {},
    ...snapshot.envKey ? { envKey: snapshot.envKey } : {},
    ...snapshot.launchSecretRef ? { launchSecretRef: snapshot.launchSecretRef } : {},
    ...snapshot.endpoint ? { endpoint: snapshot.endpoint } : {},
    ...snapshot.permissionPolicy ? { permissionPolicy: snapshot.permissionPolicy } : {},
    ...snapshot.promptTimeoutMs !== void 0 ? { promptTimeoutMs: snapshot.promptTimeoutMs } : {},
    ...snapshot.permissionTimeoutMs !== void 0 ? { permissionTimeoutMs: snapshot.permissionTimeoutMs } : {},
    ...snapshot.skills ? { skills: cloneSkillRefs(snapshot.skills) } : {},
    ...snapshot.mcpServers ? { mcpServers: cloneMcpServers(snapshot.mcpServers) } : {},
    ...snapshot.fake ? { fake: { ...snapshot.fake } } : {}
  };
}
var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([
  "connectionId",
  "provider",
  "adapterId",
  "model",
  "command",
  "args",
  "envKey",
  "launchSecretRef",
  "endpoint",
  "permissionPolicy",
  "promptTimeoutMs",
  "permissionTimeoutMs",
  "skills",
  "mcpServers",
  "fake",
  "effectiveEndpointDigest",
  "launchDigest"
]);
var SKILL_KEYS = /* @__PURE__ */ new Set(["name", "path", "enabled"]);
var MCP_KEYS = /* @__PURE__ */ new Set([
  "name",
  "transport",
  "enabled",
  "command",
  "args",
  "envKeys",
  "envSecretRefs",
  "url",
  "headerEnvKeys",
  "headerSecretRefs"
]);
var FAKE_KEYS = /* @__PURE__ */ new Set([
  "sleepMs",
  "exitCode",
  "waitForSignal",
  "emitStdout",
  "failLaunch",
  "canResume"
]);
var ENV_KEY_RE2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
var LAUNCH_SECRET_REF_RE = /^[a-z][a-z0-9-]{0,62}$/;
var SHA256_RE = /^sha256:[0-9a-f]{64}$/;
function plainRecord(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function onlyKeys(value, allowed) {
  return Object.keys(value).every((key2) => allowed.has(key2));
}
function optionalString(value) {
  return value === void 0 || typeof value === "string" && value.length > 0;
}
function optionalPositiveInt(value) {
  return value === void 0 || typeof value === "number" && Number.isInteger(value) && value > 0;
}
function stringMap(value) {
  return plainRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
}
function validSkill(value) {
  return plainRecord(value) && onlyKeys(value, SKILL_KEYS) && typeof value.name === "string" && value.name.length > 0 && optionalString(value.path) && (value.enabled === void 0 || typeof value.enabled === "boolean");
}
function validMcp(value) {
  if (!plainRecord(value) || !onlyKeys(value, MCP_KEYS)) return false;
  if (typeof value.name !== "string" || !value.name) return false;
  if (value.transport !== "stdio" && value.transport !== "http") return false;
  if (value.enabled !== void 0 && typeof value.enabled !== "boolean") return false;
  if (!optionalString(value.command) || !optionalString(value.url)) return false;
  if (value.args !== void 0 && (!Array.isArray(value.args) || !value.args.every((entry) => typeof entry === "string"))) {
    return false;
  }
  for (const key2 of ["envKeys", "envSecretRefs", "headerEnvKeys", "headerSecretRefs"]) {
    if (value[key2] !== void 0 && !stringMap(value[key2])) return false;
  }
  return true;
}
function validFake(value) {
  if (!plainRecord(value) || !onlyKeys(value, FAKE_KEYS)) return false;
  for (const key2 of ["sleepMs", "exitCode"]) {
    if (value[key2] !== void 0 && (typeof value[key2] !== "number" || !Number.isInteger(value[key2]))) return false;
  }
  for (const key2 of ["waitForSignal", "emitStdout", "canResume"]) {
    if (value[key2] !== void 0 && typeof value[key2] !== "boolean") return false;
  }
  return optionalString(value.failLaunch);
}
function safeBaseUrl(value) {
  if (value === void 0) return true;
  if (typeof value !== "string" || !value) return false;
  try {
    const url = new URL(value);
    return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password && !url.search && !url.hash;
  } catch {
    return false;
  }
}
function parseAgentConnectionSnapshot(value) {
  if (!plainRecord(value) || !onlyKeys(value, SNAPSHOT_KEYS)) return null;
  if (!isConnectionId(value.connectionId)) return null;
  if (typeof value.provider !== "string" || !value.provider) return null;
  if (typeof value.adapterId !== "string" || !value.adapterId) return null;
  for (const key2 of ["model", "command"]) {
    if (!optionalString(value[key2])) return null;
  }
  if (value.args !== void 0 && (!Array.isArray(value.args) || !value.args.every((entry) => typeof entry === "string"))) {
    return null;
  }
  for (const key2 of ["envKey"]) {
    if (value[key2] !== void 0 && (typeof value[key2] !== "string" || !ENV_KEY_RE2.test(value[key2]))) return null;
  }
  if (value.launchSecretRef !== void 0 && (typeof value.launchSecretRef !== "string" || !LAUNCH_SECRET_REF_RE.test(value.launchSecretRef))) {
    return null;
  }
  if (!safeBaseUrl(value.endpoint)) return null;
  if (value.permissionPolicy !== void 0 && value.permissionPolicy !== "allow" && value.permissionPolicy !== "ask" && value.permissionPolicy !== "deny") return null;
  if (!optionalPositiveInt(value.promptTimeoutMs) || !optionalPositiveInt(value.permissionTimeoutMs)) return null;
  if (value.skills !== void 0 && (!Array.isArray(value.skills) || !value.skills.every(validSkill))) return null;
  if (value.mcpServers !== void 0 && (!Array.isArray(value.mcpServers) || !value.mcpServers.every(validMcp))) return null;
  if (value.fake !== void 0 && !validFake(value.fake)) return null;
  if (value.effectiveEndpointDigest !== void 0 && (typeof value.effectiveEndpointDigest !== "string" || !SHA256_RE.test(value.effectiveEndpointDigest))) return null;
  if (typeof value.launchDigest !== "string" || !SHA256_RE.test(value.launchDigest)) return null;
  const snapshot = value;
  const connection = connectionConfigFromSnapshot(snapshot);
  if (calculateAgentConnectionLaunchDigest(connection, snapshot.effectiveEndpointDigest) !== snapshot.launchDigest) {
    return null;
  }
  return createAgentConnectionSnapshot(connection, {
    effectiveEndpointDigest: snapshot.effectiveEndpointDigest
  });
}

// src/core/workspace-agents.ts
import * as fs3 from "node:fs/promises";
import * as path4 from "node:path";
var WORKSPACE_AGENTS_FILENAME = "AGENTS.md";
function resolveWorkspaceAgentsPath(workspaceRoot) {
  const root = path4.resolve(workspaceRoot);
  const agentsPath = path4.resolve(root, WORKSPACE_AGENTS_FILENAME);
  if (path4.basename(agentsPath) !== WORKSPACE_AGENTS_FILENAME) {
    throw new WorkspaceAgentsError(
      "INVALID_PATH",
      `Workspace agents file must be named ${WORKSPACE_AGENTS_FILENAME}`
    );
  }
  if (path4.dirname(agentsPath) !== root) {
    throw new WorkspaceAgentsError(
      "INVALID_PATH",
      `${WORKSPACE_AGENTS_FILENAME} must be a direct child of the workspace root`
    );
  }
  return agentsPath;
}
async function loadWorkspaceAgents(workspaceRoot) {
  const agentsPath = resolveWorkspaceAgentsPath(workspaceRoot);
  try {
    const content3 = await fs3.readFile(agentsPath, "utf8");
    return {
      path: WORKSPACE_AGENTS_FILENAME,
      content: content3,
      exists: true
    };
  } catch (error) {
    if (isNotFound(error)) {
      return {
        path: WORKSPACE_AGENTS_FILENAME,
        content: "",
        exists: false
      };
    }
    throw error;
  }
}
async function writeWorkspaceAgents(workspaceRoot, content3) {
  if (typeof content3 !== "string") {
    throw new WorkspaceAgentsError("INVALID_CONTENT", "AGENTS.md content must be a string");
  }
  const agentsPath = resolveWorkspaceAgentsPath(workspaceRoot);
  const before = await loadWorkspaceAgents(workspaceRoot);
  const changed = !before.exists || before.content !== content3;
  if (!changed) {
    return {
      file: {
        path: WORKSPACE_AGENTS_FILENAME,
        content: before.content,
        exists: true
      },
      changed: false
    };
  }
  await writeTextAtomic(agentsPath, content3);
  return {
    file: {
      path: WORKSPACE_AGENTS_FILENAME,
      content: content3,
      exists: true
    },
    changed: true
  };
}
var WorkspaceAgentsError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "WorkspaceAgentsError";
  }
};
async function writeTextAtomic(filePath, content3) {
  await fs3.mkdir(path4.dirname(filePath), { recursive: true });
  const tmp = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
  try {
    await fs3.writeFile(tmp, content3, "utf8");
    await renameReplace(tmp, filePath);
  } catch (err) {
    try {
      await fs3.unlink(tmp);
    } catch {
    }
    throw err;
  }
}
async function renameReplace(tmp, filePath) {
  const attempts = process.platform === "win32" ? 8 : 1;
  let lastErr;
  for (let i = 0; i < attempts; i++) {
    try {
      await fs3.rename(tmp, filePath);
      return;
    } catch (err) {
      lastErr = err;
      if (!isRetryableRenameError(err) || i === attempts - 1) break;
      await new Promise((r) => setTimeout(r, 5 + i * 5));
    }
  }
  throw lastErr;
}
function isRetryableRenameError(err) {
  if (!err || typeof err !== "object") return false;
  const code = err.code;
  return code === "EPERM" || code === "EACCES" || code === "EBUSY" || code === "EEXIST";
}
function isNotFound(error) {
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
}

// src/service/session-context-generation.ts
async function requireResolvedRoleFromRegistry(fs21, roleId, reason) {
  const key2 = roleId.trim();
  if (!key2) {
    throw new Error(
      `contextGeneration requires a non-empty Role id (${reason})`
    );
  }
  let registry;
  try {
    registry = await loadRolesRegistry(fs21);
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new Error(
      `contextGeneration cannot load roles registry for Role "${key2}" (${reason}): ${message2}`
    );
  }
  const role = registry.roles.find((candidate) => candidate.id === key2);
  if (!role) {
    throw new Error(
      `contextGeneration required Role not found: "${key2}" (${reason})`
    );
  }
  return role;
}
async function collectStableContextGeneration(input) {
  const agents = await loadWorkspaceAgents(input.workspaceRoot);
  const agentsPointerDigest = agentsBodyCompatibilityDigest(agents.content);
  const taskSessionId = input.task.executionSessionId?.trim() || "";
  const sessionId = input.session.id.trim();
  if (!taskSessionId || taskSessionId !== sessionId) {
    throw new Error(
      `contextGeneration requires exact Task/Session binding: Task ${taskSessionId || "<none>"}, Session ${sessionId || "<none>"}`
    );
  }
  const connectionSnapshot = input.session.connectionSnapshot;
  if (!connectionSnapshot) {
    throw new Error(
      `contextGeneration requires immutable Connection snapshot on Session ${sessionId}`
    );
  }
  const connectionId = connectionSnapshot.connectionId.trim();
  const provider = connectionSnapshot.provider.trim();
  const adapterId = connectionSnapshot.adapterId.trim();
  const connectionLaunchDigest = connectionLaunchDigestFromSnapshot(connectionSnapshot);
  if (!connectionId || !provider || !adapterId || !connectionLaunchDigest) {
    throw new Error(
      "contextGeneration requires a complete immutable Agent Connection snapshot"
    );
  }
  const roleId = input.task.assigneeRoleId?.trim() || "";
  const role = roleId ? await requireResolvedRoleFromRegistry(
    input.fs,
    roleId,
    "durable Role responsibility"
  ) : void 0;
  const skillInputs = managedSkillCompatibilityInputs({
    packageRoot: input.packageRoot,
    roleId: roleId || void 0,
    role,
    packageVersion: input.packageVersion
  });
  const tentTaskBody = skillInputs.skillBodies[BUILTIN_TENT_TASK_SKILL] ?? readBundledSkillBody(input.packageRoot, BUILTIN_TENT_TASK_SKILL);
  const tentTaskVersion = skillInputs.skillVersions[BUILTIN_TENT_TASK_SKILL] ?? readBundledSkillVersion(
    input.packageRoot,
    BUILTIN_TENT_TASK_SKILL,
    input.packageVersion
  );
  const tentTaskDigest = skillBodyCompatibilityDigest({
    body: tentTaskBody,
    version: tentTaskVersion,
    name: BUILTIN_TENT_TASK_SKILL
  });
  let tentRoleBody = "";
  let tentRoleVersion = "";
  let tentRoleDigest = "";
  if (roleId) {
    tentRoleBody = skillInputs.skillBodies[BUILTIN_TENT_ROLE_SKILL] ?? readBundledSkillBody(input.packageRoot, BUILTIN_TENT_ROLE_SKILL);
    tentRoleVersion = skillInputs.skillVersions[BUILTIN_TENT_ROLE_SKILL] ?? readBundledSkillVersion(
      input.packageRoot,
      BUILTIN_TENT_ROLE_SKILL,
      input.packageVersion
    );
    tentRoleDigest = skillBodyCompatibilityDigest({
      body: tentRoleBody,
      version: tentRoleVersion,
      name: BUILTIN_TENT_ROLE_SKILL
    });
  }
  const rolePrompt = roleId ? role?.prompt?.trim() || "" : "";
  const contextGeneration = computeContextGenerationFromStableFacts({
    workspaceIdentity: input.workspaceIdentity,
    agentsBody: agents.content,
    agentsPointerDigest,
    tentRoleBody: roleId ? tentRoleBody : void 0,
    tentRoleVersion: roleId ? tentRoleVersion : void 0,
    tentTaskBody,
    tentTaskVersion,
    rolePrompt: roleId ? rolePrompt : void 0,
    connectionId,
    adapterId,
    roleId: roleId || void 0,
    capabilityFlags: input.capabilityFlags,
    connectionLaunchDigest
  });
  return {
    contextGeneration,
    agentsPointerDigest,
    tentRoleDigest,
    tentRoleVersion,
    tentTaskDigest,
    tentTaskVersion,
    rolePrompt,
    connectionLaunchDigest
  };
}
function connectionLaunchDigestFromSnapshot(connection) {
  const digest = connection.launchDigest.trim();
  if (!digest) {
    throw new Error("Agent Connection snapshot is missing launchDigest");
  }
  const expected = calculateAgentConnectionLaunchDigest(
    connectionConfigFromSnapshot(connection),
    connection.effectiveEndpointDigest
  );
  if (digest !== expected) {
    throw new Error(
      `Agent Connection snapshot launchDigest mismatch for ${connection.connectionId}`
    );
  }
  return digest;
}
function appendCallerBootstrapSection(officialManagedBootstrap, callerAppend) {
  const base = officialManagedBootstrap.trimEnd();
  const append = typeof callerAppend === "string" ? callerAppend.trim() : "";
  if (!append) return base ? `${base}
` : "";
  return `${base}

--- Caller bootstrap append ---
${append}
`;
}

// src/markdown/attachments.ts
import { createHash as createHash4 } from "node:crypto";
import * as nodePath from "node:path";
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
function sanitizeAttachmentFileName(fileName) {
  const source = fileName.normalize("NFKC");
  if (!source.trim()) throw new Error("Attachment file name is required");
  if (source.includes("/") || source.includes("\\") || source === "." || source === "..") {
    throw new Error("Attachment file name must be a single path segment");
  }
  let clean = source.replace(/[<>:"|?*\x00-\x1f]/g, "_").replace(/[. ]+$/g, "");
  if (!clean || clean === "." || clean === "..") clean = "file";
  if (clean.length > 120) {
    const ext2 = nodePath.posix.extname(clean).slice(0, 20);
    const stem2 = clean.slice(0, Math.max(1, 120 - ext2.length));
    clean = `${stem2}${ext2}`;
  }
  const ext = nodePath.posix.extname(clean);
  const stem = ext ? clean.slice(0, -ext.length) : clean;
  if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(stem) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(clean)) {
    clean = `file-${clean}`;
  }
  return clean;
}
function decodeBase64Strict(b64) {
  const compact = b64.replace(/\s+/g, "");
  if (!compact) throw new Error("Invalid base64: empty payload");
  if (!/^[A-Za-z0-9+/]+={0,2}$/.test(compact)) {
    throw new Error("Invalid base64: non-alphabet characters");
  }
  if (compact.length % 4 !== 0) {
    throw new Error("Invalid base64: length must be a multiple of 4");
  }
  let buf;
  try {
    buf = Buffer.from(compact, "base64");
  } catch {
    throw new Error("Invalid base64: decode failed");
  }
  const reencoded = buf.toString("base64");
  if (reencoded !== compact) {
    throw new Error("Invalid base64: strict decode mismatch");
  }
  return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
}
function assertAttachmentSize(byteLength) {
  if (byteLength < 0 || !Number.isFinite(byteLength)) {
    throw new Error("Invalid attachment size");
  }
  if (byteLength > MAX_ATTACHMENT_BYTES) {
    throw new Error(
      `Attachment exceeds max size of ${MAX_ATTACHMENT_BYTES} bytes (${byteLength} bytes)`
    );
  }
}
function contentId(bytes) {
  return createHash4("sha256").update(Buffer.from(bytes)).digest("hex").slice(0, 12);
}
function attachmentRelativePath(nodeId, safeName, bytes) {
  const id = contentId(bytes);
  const ext = nodePath.posix.extname(safeName);
  const base = nodePath.posix.basename(safeName, ext) || "file";
  return `${ATTACHMENTS_DIR}/${nodeId}/${base}-${id}${ext}`;
}
function bytesEqual(a, b) {
  if (a.byteLength !== b.byteLength) return false;
  for (let i = 0; i < a.byteLength; i++) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}
async function storeAttachmentBytes(fs21, nodeId, fileName, bytes, sourceNotePath) {
  if (!nodeId.trim()) throw new Error("Node id is required");
  const safe = sanitizeAttachmentFileName(fileName);
  assertAttachmentSize(bytes.byteLength);
  const rel = attachmentRelativePath(nodeId, safe, bytes);
  const normalized = rel.replace(/\\/g, "/");
  if (!normalized.startsWith(`${ATTACHMENTS_DIR}/${nodeId}/`) || normalized.split("/").some((p) => p === ".." || p === "")) {
    throw new Error(`Attachment path rejected: ${rel}`);
  }
  if (await fs21.exists(rel)) {
    const existing = await fs21.readBinary(rel);
    if (!bytesEqual(existing, bytes)) {
      throw new Error(`Attachment content-address collision at ${rel}`);
    }
    return attachmentResult(rel, sourceNotePath);
  }
  await fs21.writeBinary(rel, bytes);
  return attachmentResult(rel, sourceNotePath);
}
function markdownAttachmentDestination(destination) {
  if (!/[\s<>()]/.test(destination)) return destination;
  return `<${destination.replace(/</g, "%3C").replace(/>/g, "%3E")}>`;
}
function attachmentResult(relativePath4, sourceNotePath) {
  const target = sourceNotePath ? nodePath.posix.relative(nodePath.posix.dirname(sourceNotePath.replace(/\\/g, "/")), relativePath4) : relativePath4;
  return {
    relativePath: relativePath4,
    markdown: `![](${markdownAttachmentDestination(target)})`
  };
}

// src/adapters/acp/image-prompt.ts
import * as nodePath2 from "node:path";
import { pathToFileURL } from "node:url";

// node_modules/mdast-util-to-string/lib/index.js
var emptyOptions = {};
function toString(value, options) {
  const settings = options || emptyOptions;
  const includeImageAlt = typeof settings.includeImageAlt === "boolean" ? settings.includeImageAlt : true;
  const includeHtml = typeof settings.includeHtml === "boolean" ? settings.includeHtml : true;
  return one(value, includeImageAlt, includeHtml);
}
function one(value, includeImageAlt, includeHtml) {
  if (node(value)) {
    if ("value" in value) {
      return value.type === "html" && !includeHtml ? "" : value.value;
    }
    if (includeImageAlt && "alt" in value && value.alt) {
      return value.alt;
    }
    if ("children" in value) {
      return all(value.children, includeImageAlt, includeHtml);
    }
  }
  if (Array.isArray(value)) {
    return all(value, includeImageAlt, includeHtml);
  }
  return "";
}
function all(values, includeImageAlt, includeHtml) {
  const result = [];
  let index2 = -1;
  while (++index2 < values.length) {
    result[index2] = one(values[index2], includeImageAlt, includeHtml);
  }
  return result.join("");
}
function node(value) {
  return Boolean(value && typeof value === "object");
}

// node_modules/character-entities/index.js
var characterEntities = {
  AElig: "\xC6",
  AMP: "&",
  Aacute: "\xC1",
  Abreve: "\u0102",
  Acirc: "\xC2",
  Acy: "\u0410",
  Afr: "\u{1D504}",
  Agrave: "\xC0",
  Alpha: "\u0391",
  Amacr: "\u0100",
  And: "\u2A53",
  Aogon: "\u0104",
  Aopf: "\u{1D538}",
  ApplyFunction: "\u2061",
  Aring: "\xC5",
  Ascr: "\u{1D49C}",
  Assign: "\u2254",
  Atilde: "\xC3",
  Auml: "\xC4",
  Backslash: "\u2216",
  Barv: "\u2AE7",
  Barwed: "\u2306",
  Bcy: "\u0411",
  Because: "\u2235",
  Bernoullis: "\u212C",
  Beta: "\u0392",
  Bfr: "\u{1D505}",
  Bopf: "\u{1D539}",
  Breve: "\u02D8",
  Bscr: "\u212C",
  Bumpeq: "\u224E",
  CHcy: "\u0427",
  COPY: "\xA9",
  Cacute: "\u0106",
  Cap: "\u22D2",
  CapitalDifferentialD: "\u2145",
  Cayleys: "\u212D",
  Ccaron: "\u010C",
  Ccedil: "\xC7",
  Ccirc: "\u0108",
  Cconint: "\u2230",
  Cdot: "\u010A",
  Cedilla: "\xB8",
  CenterDot: "\xB7",
  Cfr: "\u212D",
  Chi: "\u03A7",
  CircleDot: "\u2299",
  CircleMinus: "\u2296",
  CirclePlus: "\u2295",
  CircleTimes: "\u2297",
  ClockwiseContourIntegral: "\u2232",
  CloseCurlyDoubleQuote: "\u201D",
  CloseCurlyQuote: "\u2019",
  Colon: "\u2237",
  Colone: "\u2A74",
  Congruent: "\u2261",
  Conint: "\u222F",
  ContourIntegral: "\u222E",
  Copf: "\u2102",
  Coproduct: "\u2210",
  CounterClockwiseContourIntegral: "\u2233",
  Cross: "\u2A2F",
  Cscr: "\u{1D49E}",
  Cup: "\u22D3",
  CupCap: "\u224D",
  DD: "\u2145",
  DDotrahd: "\u2911",
  DJcy: "\u0402",
  DScy: "\u0405",
  DZcy: "\u040F",
  Dagger: "\u2021",
  Darr: "\u21A1",
  Dashv: "\u2AE4",
  Dcaron: "\u010E",
  Dcy: "\u0414",
  Del: "\u2207",
  Delta: "\u0394",
  Dfr: "\u{1D507}",
  DiacriticalAcute: "\xB4",
  DiacriticalDot: "\u02D9",
  DiacriticalDoubleAcute: "\u02DD",
  DiacriticalGrave: "`",
  DiacriticalTilde: "\u02DC",
  Diamond: "\u22C4",
  DifferentialD: "\u2146",
  Dopf: "\u{1D53B}",
  Dot: "\xA8",
  DotDot: "\u20DC",
  DotEqual: "\u2250",
  DoubleContourIntegral: "\u222F",
  DoubleDot: "\xA8",
  DoubleDownArrow: "\u21D3",
  DoubleLeftArrow: "\u21D0",
  DoubleLeftRightArrow: "\u21D4",
  DoubleLeftTee: "\u2AE4",
  DoubleLongLeftArrow: "\u27F8",
  DoubleLongLeftRightArrow: "\u27FA",
  DoubleLongRightArrow: "\u27F9",
  DoubleRightArrow: "\u21D2",
  DoubleRightTee: "\u22A8",
  DoubleUpArrow: "\u21D1",
  DoubleUpDownArrow: "\u21D5",
  DoubleVerticalBar: "\u2225",
  DownArrow: "\u2193",
  DownArrowBar: "\u2913",
  DownArrowUpArrow: "\u21F5",
  DownBreve: "\u0311",
  DownLeftRightVector: "\u2950",
  DownLeftTeeVector: "\u295E",
  DownLeftVector: "\u21BD",
  DownLeftVectorBar: "\u2956",
  DownRightTeeVector: "\u295F",
  DownRightVector: "\u21C1",
  DownRightVectorBar: "\u2957",
  DownTee: "\u22A4",
  DownTeeArrow: "\u21A7",
  Downarrow: "\u21D3",
  Dscr: "\u{1D49F}",
  Dstrok: "\u0110",
  ENG: "\u014A",
  ETH: "\xD0",
  Eacute: "\xC9",
  Ecaron: "\u011A",
  Ecirc: "\xCA",
  Ecy: "\u042D",
  Edot: "\u0116",
  Efr: "\u{1D508}",
  Egrave: "\xC8",
  Element: "\u2208",
  Emacr: "\u0112",
  EmptySmallSquare: "\u25FB",
  EmptyVerySmallSquare: "\u25AB",
  Eogon: "\u0118",
  Eopf: "\u{1D53C}",
  Epsilon: "\u0395",
  Equal: "\u2A75",
  EqualTilde: "\u2242",
  Equilibrium: "\u21CC",
  Escr: "\u2130",
  Esim: "\u2A73",
  Eta: "\u0397",
  Euml: "\xCB",
  Exists: "\u2203",
  ExponentialE: "\u2147",
  Fcy: "\u0424",
  Ffr: "\u{1D509}",
  FilledSmallSquare: "\u25FC",
  FilledVerySmallSquare: "\u25AA",
  Fopf: "\u{1D53D}",
  ForAll: "\u2200",
  Fouriertrf: "\u2131",
  Fscr: "\u2131",
  GJcy: "\u0403",
  GT: ">",
  Gamma: "\u0393",
  Gammad: "\u03DC",
  Gbreve: "\u011E",
  Gcedil: "\u0122",
  Gcirc: "\u011C",
  Gcy: "\u0413",
  Gdot: "\u0120",
  Gfr: "\u{1D50A}",
  Gg: "\u22D9",
  Gopf: "\u{1D53E}",
  GreaterEqual: "\u2265",
  GreaterEqualLess: "\u22DB",
  GreaterFullEqual: "\u2267",
  GreaterGreater: "\u2AA2",
  GreaterLess: "\u2277",
  GreaterSlantEqual: "\u2A7E",
  GreaterTilde: "\u2273",
  Gscr: "\u{1D4A2}",
  Gt: "\u226B",
  HARDcy: "\u042A",
  Hacek: "\u02C7",
  Hat: "^",
  Hcirc: "\u0124",
  Hfr: "\u210C",
  HilbertSpace: "\u210B",
  Hopf: "\u210D",
  HorizontalLine: "\u2500",
  Hscr: "\u210B",
  Hstrok: "\u0126",
  HumpDownHump: "\u224E",
  HumpEqual: "\u224F",
  IEcy: "\u0415",
  IJlig: "\u0132",
  IOcy: "\u0401",
  Iacute: "\xCD",
  Icirc: "\xCE",
  Icy: "\u0418",
  Idot: "\u0130",
  Ifr: "\u2111",
  Igrave: "\xCC",
  Im: "\u2111",
  Imacr: "\u012A",
  ImaginaryI: "\u2148",
  Implies: "\u21D2",
  Int: "\u222C",
  Integral: "\u222B",
  Intersection: "\u22C2",
  InvisibleComma: "\u2063",
  InvisibleTimes: "\u2062",
  Iogon: "\u012E",
  Iopf: "\u{1D540}",
  Iota: "\u0399",
  Iscr: "\u2110",
  Itilde: "\u0128",
  Iukcy: "\u0406",
  Iuml: "\xCF",
  Jcirc: "\u0134",
  Jcy: "\u0419",
  Jfr: "\u{1D50D}",
  Jopf: "\u{1D541}",
  Jscr: "\u{1D4A5}",
  Jsercy: "\u0408",
  Jukcy: "\u0404",
  KHcy: "\u0425",
  KJcy: "\u040C",
  Kappa: "\u039A",
  Kcedil: "\u0136",
  Kcy: "\u041A",
  Kfr: "\u{1D50E}",
  Kopf: "\u{1D542}",
  Kscr: "\u{1D4A6}",
  LJcy: "\u0409",
  LT: "<",
  Lacute: "\u0139",
  Lambda: "\u039B",
  Lang: "\u27EA",
  Laplacetrf: "\u2112",
  Larr: "\u219E",
  Lcaron: "\u013D",
  Lcedil: "\u013B",
  Lcy: "\u041B",
  LeftAngleBracket: "\u27E8",
  LeftArrow: "\u2190",
  LeftArrowBar: "\u21E4",
  LeftArrowRightArrow: "\u21C6",
  LeftCeiling: "\u2308",
  LeftDoubleBracket: "\u27E6",
  LeftDownTeeVector: "\u2961",
  LeftDownVector: "\u21C3",
  LeftDownVectorBar: "\u2959",
  LeftFloor: "\u230A",
  LeftRightArrow: "\u2194",
  LeftRightVector: "\u294E",
  LeftTee: "\u22A3",
  LeftTeeArrow: "\u21A4",
  LeftTeeVector: "\u295A",
  LeftTriangle: "\u22B2",
  LeftTriangleBar: "\u29CF",
  LeftTriangleEqual: "\u22B4",
  LeftUpDownVector: "\u2951",
  LeftUpTeeVector: "\u2960",
  LeftUpVector: "\u21BF",
  LeftUpVectorBar: "\u2958",
  LeftVector: "\u21BC",
  LeftVectorBar: "\u2952",
  Leftarrow: "\u21D0",
  Leftrightarrow: "\u21D4",
  LessEqualGreater: "\u22DA",
  LessFullEqual: "\u2266",
  LessGreater: "\u2276",
  LessLess: "\u2AA1",
  LessSlantEqual: "\u2A7D",
  LessTilde: "\u2272",
  Lfr: "\u{1D50F}",
  Ll: "\u22D8",
  Lleftarrow: "\u21DA",
  Lmidot: "\u013F",
  LongLeftArrow: "\u27F5",
  LongLeftRightArrow: "\u27F7",
  LongRightArrow: "\u27F6",
  Longleftarrow: "\u27F8",
  Longleftrightarrow: "\u27FA",
  Longrightarrow: "\u27F9",
  Lopf: "\u{1D543}",
  LowerLeftArrow: "\u2199",
  LowerRightArrow: "\u2198",
  Lscr: "\u2112",
  Lsh: "\u21B0",
  Lstrok: "\u0141",
  Lt: "\u226A",
  Map: "\u2905",
  Mcy: "\u041C",
  MediumSpace: "\u205F",
  Mellintrf: "\u2133",
  Mfr: "\u{1D510}",
  MinusPlus: "\u2213",
  Mopf: "\u{1D544}",
  Mscr: "\u2133",
  Mu: "\u039C",
  NJcy: "\u040A",
  Nacute: "\u0143",
  Ncaron: "\u0147",
  Ncedil: "\u0145",
  Ncy: "\u041D",
  NegativeMediumSpace: "\u200B",
  NegativeThickSpace: "\u200B",
  NegativeThinSpace: "\u200B",
  NegativeVeryThinSpace: "\u200B",
  NestedGreaterGreater: "\u226B",
  NestedLessLess: "\u226A",
  NewLine: "\n",
  Nfr: "\u{1D511}",
  NoBreak: "\u2060",
  NonBreakingSpace: "\xA0",
  Nopf: "\u2115",
  Not: "\u2AEC",
  NotCongruent: "\u2262",
  NotCupCap: "\u226D",
  NotDoubleVerticalBar: "\u2226",
  NotElement: "\u2209",
  NotEqual: "\u2260",
  NotEqualTilde: "\u2242\u0338",
  NotExists: "\u2204",
  NotGreater: "\u226F",
  NotGreaterEqual: "\u2271",
  NotGreaterFullEqual: "\u2267\u0338",
  NotGreaterGreater: "\u226B\u0338",
  NotGreaterLess: "\u2279",
  NotGreaterSlantEqual: "\u2A7E\u0338",
  NotGreaterTilde: "\u2275",
  NotHumpDownHump: "\u224E\u0338",
  NotHumpEqual: "\u224F\u0338",
  NotLeftTriangle: "\u22EA",
  NotLeftTriangleBar: "\u29CF\u0338",
  NotLeftTriangleEqual: "\u22EC",
  NotLess: "\u226E",
  NotLessEqual: "\u2270",
  NotLessGreater: "\u2278",
  NotLessLess: "\u226A\u0338",
  NotLessSlantEqual: "\u2A7D\u0338",
  NotLessTilde: "\u2274",
  NotNestedGreaterGreater: "\u2AA2\u0338",
  NotNestedLessLess: "\u2AA1\u0338",
  NotPrecedes: "\u2280",
  NotPrecedesEqual: "\u2AAF\u0338",
  NotPrecedesSlantEqual: "\u22E0",
  NotReverseElement: "\u220C",
  NotRightTriangle: "\u22EB",
  NotRightTriangleBar: "\u29D0\u0338",
  NotRightTriangleEqual: "\u22ED",
  NotSquareSubset: "\u228F\u0338",
  NotSquareSubsetEqual: "\u22E2",
  NotSquareSuperset: "\u2290\u0338",
  NotSquareSupersetEqual: "\u22E3",
  NotSubset: "\u2282\u20D2",
  NotSubsetEqual: "\u2288",
  NotSucceeds: "\u2281",
  NotSucceedsEqual: "\u2AB0\u0338",
  NotSucceedsSlantEqual: "\u22E1",
  NotSucceedsTilde: "\u227F\u0338",
  NotSuperset: "\u2283\u20D2",
  NotSupersetEqual: "\u2289",
  NotTilde: "\u2241",
  NotTildeEqual: "\u2244",
  NotTildeFullEqual: "\u2247",
  NotTildeTilde: "\u2249",
  NotVerticalBar: "\u2224",
  Nscr: "\u{1D4A9}",
  Ntilde: "\xD1",
  Nu: "\u039D",
  OElig: "\u0152",
  Oacute: "\xD3",
  Ocirc: "\xD4",
  Ocy: "\u041E",
  Odblac: "\u0150",
  Ofr: "\u{1D512}",
  Ograve: "\xD2",
  Omacr: "\u014C",
  Omega: "\u03A9",
  Omicron: "\u039F",
  Oopf: "\u{1D546}",
  OpenCurlyDoubleQuote: "\u201C",
  OpenCurlyQuote: "\u2018",
  Or: "\u2A54",
  Oscr: "\u{1D4AA}",
  Oslash: "\xD8",
  Otilde: "\xD5",
  Otimes: "\u2A37",
  Ouml: "\xD6",
  OverBar: "\u203E",
  OverBrace: "\u23DE",
  OverBracket: "\u23B4",
  OverParenthesis: "\u23DC",
  PartialD: "\u2202",
  Pcy: "\u041F",
  Pfr: "\u{1D513}",
  Phi: "\u03A6",
  Pi: "\u03A0",
  PlusMinus: "\xB1",
  Poincareplane: "\u210C",
  Popf: "\u2119",
  Pr: "\u2ABB",
  Precedes: "\u227A",
  PrecedesEqual: "\u2AAF",
  PrecedesSlantEqual: "\u227C",
  PrecedesTilde: "\u227E",
  Prime: "\u2033",
  Product: "\u220F",
  Proportion: "\u2237",
  Proportional: "\u221D",
  Pscr: "\u{1D4AB}",
  Psi: "\u03A8",
  QUOT: '"',
  Qfr: "\u{1D514}",
  Qopf: "\u211A",
  Qscr: "\u{1D4AC}",
  RBarr: "\u2910",
  REG: "\xAE",
  Racute: "\u0154",
  Rang: "\u27EB",
  Rarr: "\u21A0",
  Rarrtl: "\u2916",
  Rcaron: "\u0158",
  Rcedil: "\u0156",
  Rcy: "\u0420",
  Re: "\u211C",
  ReverseElement: "\u220B",
  ReverseEquilibrium: "\u21CB",
  ReverseUpEquilibrium: "\u296F",
  Rfr: "\u211C",
  Rho: "\u03A1",
  RightAngleBracket: "\u27E9",
  RightArrow: "\u2192",
  RightArrowBar: "\u21E5",
  RightArrowLeftArrow: "\u21C4",
  RightCeiling: "\u2309",
  RightDoubleBracket: "\u27E7",
  RightDownTeeVector: "\u295D",
  RightDownVector: "\u21C2",
  RightDownVectorBar: "\u2955",
  RightFloor: "\u230B",
  RightTee: "\u22A2",
  RightTeeArrow: "\u21A6",
  RightTeeVector: "\u295B",
  RightTriangle: "\u22B3",
  RightTriangleBar: "\u29D0",
  RightTriangleEqual: "\u22B5",
  RightUpDownVector: "\u294F",
  RightUpTeeVector: "\u295C",
  RightUpVector: "\u21BE",
  RightUpVectorBar: "\u2954",
  RightVector: "\u21C0",
  RightVectorBar: "\u2953",
  Rightarrow: "\u21D2",
  Ropf: "\u211D",
  RoundImplies: "\u2970",
  Rrightarrow: "\u21DB",
  Rscr: "\u211B",
  Rsh: "\u21B1",
  RuleDelayed: "\u29F4",
  SHCHcy: "\u0429",
  SHcy: "\u0428",
  SOFTcy: "\u042C",
  Sacute: "\u015A",
  Sc: "\u2ABC",
  Scaron: "\u0160",
  Scedil: "\u015E",
  Scirc: "\u015C",
  Scy: "\u0421",
  Sfr: "\u{1D516}",
  ShortDownArrow: "\u2193",
  ShortLeftArrow: "\u2190",
  ShortRightArrow: "\u2192",
  ShortUpArrow: "\u2191",
  Sigma: "\u03A3",
  SmallCircle: "\u2218",
  Sopf: "\u{1D54A}",
  Sqrt: "\u221A",
  Square: "\u25A1",
  SquareIntersection: "\u2293",
  SquareSubset: "\u228F",
  SquareSubsetEqual: "\u2291",
  SquareSuperset: "\u2290",
  SquareSupersetEqual: "\u2292",
  SquareUnion: "\u2294",
  Sscr: "\u{1D4AE}",
  Star: "\u22C6",
  Sub: "\u22D0",
  Subset: "\u22D0",
  SubsetEqual: "\u2286",
  Succeeds: "\u227B",
  SucceedsEqual: "\u2AB0",
  SucceedsSlantEqual: "\u227D",
  SucceedsTilde: "\u227F",
  SuchThat: "\u220B",
  Sum: "\u2211",
  Sup: "\u22D1",
  Superset: "\u2283",
  SupersetEqual: "\u2287",
  Supset: "\u22D1",
  THORN: "\xDE",
  TRADE: "\u2122",
  TSHcy: "\u040B",
  TScy: "\u0426",
  Tab: "	",
  Tau: "\u03A4",
  Tcaron: "\u0164",
  Tcedil: "\u0162",
  Tcy: "\u0422",
  Tfr: "\u{1D517}",
  Therefore: "\u2234",
  Theta: "\u0398",
  ThickSpace: "\u205F\u200A",
  ThinSpace: "\u2009",
  Tilde: "\u223C",
  TildeEqual: "\u2243",
  TildeFullEqual: "\u2245",
  TildeTilde: "\u2248",
  Topf: "\u{1D54B}",
  TripleDot: "\u20DB",
  Tscr: "\u{1D4AF}",
  Tstrok: "\u0166",
  Uacute: "\xDA",
  Uarr: "\u219F",
  Uarrocir: "\u2949",
  Ubrcy: "\u040E",
  Ubreve: "\u016C",
  Ucirc: "\xDB",
  Ucy: "\u0423",
  Udblac: "\u0170",
  Ufr: "\u{1D518}",
  Ugrave: "\xD9",
  Umacr: "\u016A",
  UnderBar: "_",
  UnderBrace: "\u23DF",
  UnderBracket: "\u23B5",
  UnderParenthesis: "\u23DD",
  Union: "\u22C3",
  UnionPlus: "\u228E",
  Uogon: "\u0172",
  Uopf: "\u{1D54C}",
  UpArrow: "\u2191",
  UpArrowBar: "\u2912",
  UpArrowDownArrow: "\u21C5",
  UpDownArrow: "\u2195",
  UpEquilibrium: "\u296E",
  UpTee: "\u22A5",
  UpTeeArrow: "\u21A5",
  Uparrow: "\u21D1",
  Updownarrow: "\u21D5",
  UpperLeftArrow: "\u2196",
  UpperRightArrow: "\u2197",
  Upsi: "\u03D2",
  Upsilon: "\u03A5",
  Uring: "\u016E",
  Uscr: "\u{1D4B0}",
  Utilde: "\u0168",
  Uuml: "\xDC",
  VDash: "\u22AB",
  Vbar: "\u2AEB",
  Vcy: "\u0412",
  Vdash: "\u22A9",
  Vdashl: "\u2AE6",
  Vee: "\u22C1",
  Verbar: "\u2016",
  Vert: "\u2016",
  VerticalBar: "\u2223",
  VerticalLine: "|",
  VerticalSeparator: "\u2758",
  VerticalTilde: "\u2240",
  VeryThinSpace: "\u200A",
  Vfr: "\u{1D519}",
  Vopf: "\u{1D54D}",
  Vscr: "\u{1D4B1}",
  Vvdash: "\u22AA",
  Wcirc: "\u0174",
  Wedge: "\u22C0",
  Wfr: "\u{1D51A}",
  Wopf: "\u{1D54E}",
  Wscr: "\u{1D4B2}",
  Xfr: "\u{1D51B}",
  Xi: "\u039E",
  Xopf: "\u{1D54F}",
  Xscr: "\u{1D4B3}",
  YAcy: "\u042F",
  YIcy: "\u0407",
  YUcy: "\u042E",
  Yacute: "\xDD",
  Ycirc: "\u0176",
  Ycy: "\u042B",
  Yfr: "\u{1D51C}",
  Yopf: "\u{1D550}",
  Yscr: "\u{1D4B4}",
  Yuml: "\u0178",
  ZHcy: "\u0416",
  Zacute: "\u0179",
  Zcaron: "\u017D",
  Zcy: "\u0417",
  Zdot: "\u017B",
  ZeroWidthSpace: "\u200B",
  Zeta: "\u0396",
  Zfr: "\u2128",
  Zopf: "\u2124",
  Zscr: "\u{1D4B5}",
  aacute: "\xE1",
  abreve: "\u0103",
  ac: "\u223E",
  acE: "\u223E\u0333",
  acd: "\u223F",
  acirc: "\xE2",
  acute: "\xB4",
  acy: "\u0430",
  aelig: "\xE6",
  af: "\u2061",
  afr: "\u{1D51E}",
  agrave: "\xE0",
  alefsym: "\u2135",
  aleph: "\u2135",
  alpha: "\u03B1",
  amacr: "\u0101",
  amalg: "\u2A3F",
  amp: "&",
  and: "\u2227",
  andand: "\u2A55",
  andd: "\u2A5C",
  andslope: "\u2A58",
  andv: "\u2A5A",
  ang: "\u2220",
  ange: "\u29A4",
  angle: "\u2220",
  angmsd: "\u2221",
  angmsdaa: "\u29A8",
  angmsdab: "\u29A9",
  angmsdac: "\u29AA",
  angmsdad: "\u29AB",
  angmsdae: "\u29AC",
  angmsdaf: "\u29AD",
  angmsdag: "\u29AE",
  angmsdah: "\u29AF",
  angrt: "\u221F",
  angrtvb: "\u22BE",
  angrtvbd: "\u299D",
  angsph: "\u2222",
  angst: "\xC5",
  angzarr: "\u237C",
  aogon: "\u0105",
  aopf: "\u{1D552}",
  ap: "\u2248",
  apE: "\u2A70",
  apacir: "\u2A6F",
  ape: "\u224A",
  apid: "\u224B",
  apos: "'",
  approx: "\u2248",
  approxeq: "\u224A",
  aring: "\xE5",
  ascr: "\u{1D4B6}",
  ast: "*",
  asymp: "\u2248",
  asympeq: "\u224D",
  atilde: "\xE3",
  auml: "\xE4",
  awconint: "\u2233",
  awint: "\u2A11",
  bNot: "\u2AED",
  backcong: "\u224C",
  backepsilon: "\u03F6",
  backprime: "\u2035",
  backsim: "\u223D",
  backsimeq: "\u22CD",
  barvee: "\u22BD",
  barwed: "\u2305",
  barwedge: "\u2305",
  bbrk: "\u23B5",
  bbrktbrk: "\u23B6",
  bcong: "\u224C",
  bcy: "\u0431",
  bdquo: "\u201E",
  becaus: "\u2235",
  because: "\u2235",
  bemptyv: "\u29B0",
  bepsi: "\u03F6",
  bernou: "\u212C",
  beta: "\u03B2",
  beth: "\u2136",
  between: "\u226C",
  bfr: "\u{1D51F}",
  bigcap: "\u22C2",
  bigcirc: "\u25EF",
  bigcup: "\u22C3",
  bigodot: "\u2A00",
  bigoplus: "\u2A01",
  bigotimes: "\u2A02",
  bigsqcup: "\u2A06",
  bigstar: "\u2605",
  bigtriangledown: "\u25BD",
  bigtriangleup: "\u25B3",
  biguplus: "\u2A04",
  bigvee: "\u22C1",
  bigwedge: "\u22C0",
  bkarow: "\u290D",
  blacklozenge: "\u29EB",
  blacksquare: "\u25AA",
  blacktriangle: "\u25B4",
  blacktriangledown: "\u25BE",
  blacktriangleleft: "\u25C2",
  blacktriangleright: "\u25B8",
  blank: "\u2423",
  blk12: "\u2592",
  blk14: "\u2591",
  blk34: "\u2593",
  block: "\u2588",
  bne: "=\u20E5",
  bnequiv: "\u2261\u20E5",
  bnot: "\u2310",
  bopf: "\u{1D553}",
  bot: "\u22A5",
  bottom: "\u22A5",
  bowtie: "\u22C8",
  boxDL: "\u2557",
  boxDR: "\u2554",
  boxDl: "\u2556",
  boxDr: "\u2553",
  boxH: "\u2550",
  boxHD: "\u2566",
  boxHU: "\u2569",
  boxHd: "\u2564",
  boxHu: "\u2567",
  boxUL: "\u255D",
  boxUR: "\u255A",
  boxUl: "\u255C",
  boxUr: "\u2559",
  boxV: "\u2551",
  boxVH: "\u256C",
  boxVL: "\u2563",
  boxVR: "\u2560",
  boxVh: "\u256B",
  boxVl: "\u2562",
  boxVr: "\u255F",
  boxbox: "\u29C9",
  boxdL: "\u2555",
  boxdR: "\u2552",
  boxdl: "\u2510",
  boxdr: "\u250C",
  boxh: "\u2500",
  boxhD: "\u2565",
  boxhU: "\u2568",
  boxhd: "\u252C",
  boxhu: "\u2534",
  boxminus: "\u229F",
  boxplus: "\u229E",
  boxtimes: "\u22A0",
  boxuL: "\u255B",
  boxuR: "\u2558",
  boxul: "\u2518",
  boxur: "\u2514",
  boxv: "\u2502",
  boxvH: "\u256A",
  boxvL: "\u2561",
  boxvR: "\u255E",
  boxvh: "\u253C",
  boxvl: "\u2524",
  boxvr: "\u251C",
  bprime: "\u2035",
  breve: "\u02D8",
  brvbar: "\xA6",
  bscr: "\u{1D4B7}",
  bsemi: "\u204F",
  bsim: "\u223D",
  bsime: "\u22CD",
  bsol: "\\",
  bsolb: "\u29C5",
  bsolhsub: "\u27C8",
  bull: "\u2022",
  bullet: "\u2022",
  bump: "\u224E",
  bumpE: "\u2AAE",
  bumpe: "\u224F",
  bumpeq: "\u224F",
  cacute: "\u0107",
  cap: "\u2229",
  capand: "\u2A44",
  capbrcup: "\u2A49",
  capcap: "\u2A4B",
  capcup: "\u2A47",
  capdot: "\u2A40",
  caps: "\u2229\uFE00",
  caret: "\u2041",
  caron: "\u02C7",
  ccaps: "\u2A4D",
  ccaron: "\u010D",
  ccedil: "\xE7",
  ccirc: "\u0109",
  ccups: "\u2A4C",
  ccupssm: "\u2A50",
  cdot: "\u010B",
  cedil: "\xB8",
  cemptyv: "\u29B2",
  cent: "\xA2",
  centerdot: "\xB7",
  cfr: "\u{1D520}",
  chcy: "\u0447",
  check: "\u2713",
  checkmark: "\u2713",
  chi: "\u03C7",
  cir: "\u25CB",
  cirE: "\u29C3",
  circ: "\u02C6",
  circeq: "\u2257",
  circlearrowleft: "\u21BA",
  circlearrowright: "\u21BB",
  circledR: "\xAE",
  circledS: "\u24C8",
  circledast: "\u229B",
  circledcirc: "\u229A",
  circleddash: "\u229D",
  cire: "\u2257",
  cirfnint: "\u2A10",
  cirmid: "\u2AEF",
  cirscir: "\u29C2",
  clubs: "\u2663",
  clubsuit: "\u2663",
  colon: ":",
  colone: "\u2254",
  coloneq: "\u2254",
  comma: ",",
  commat: "@",
  comp: "\u2201",
  compfn: "\u2218",
  complement: "\u2201",
  complexes: "\u2102",
  cong: "\u2245",
  congdot: "\u2A6D",
  conint: "\u222E",
  copf: "\u{1D554}",
  coprod: "\u2210",
  copy: "\xA9",
  copysr: "\u2117",
  crarr: "\u21B5",
  cross: "\u2717",
  cscr: "\u{1D4B8}",
  csub: "\u2ACF",
  csube: "\u2AD1",
  csup: "\u2AD0",
  csupe: "\u2AD2",
  ctdot: "\u22EF",
  cudarrl: "\u2938",
  cudarrr: "\u2935",
  cuepr: "\u22DE",
  cuesc: "\u22DF",
  cularr: "\u21B6",
  cularrp: "\u293D",
  cup: "\u222A",
  cupbrcap: "\u2A48",
  cupcap: "\u2A46",
  cupcup: "\u2A4A",
  cupdot: "\u228D",
  cupor: "\u2A45",
  cups: "\u222A\uFE00",
  curarr: "\u21B7",
  curarrm: "\u293C",
  curlyeqprec: "\u22DE",
  curlyeqsucc: "\u22DF",
  curlyvee: "\u22CE",
  curlywedge: "\u22CF",
  curren: "\xA4",
  curvearrowleft: "\u21B6",
  curvearrowright: "\u21B7",
  cuvee: "\u22CE",
  cuwed: "\u22CF",
  cwconint: "\u2232",
  cwint: "\u2231",
  cylcty: "\u232D",
  dArr: "\u21D3",
  dHar: "\u2965",
  dagger: "\u2020",
  daleth: "\u2138",
  darr: "\u2193",
  dash: "\u2010",
  dashv: "\u22A3",
  dbkarow: "\u290F",
  dblac: "\u02DD",
  dcaron: "\u010F",
  dcy: "\u0434",
  dd: "\u2146",
  ddagger: "\u2021",
  ddarr: "\u21CA",
  ddotseq: "\u2A77",
  deg: "\xB0",
  delta: "\u03B4",
  demptyv: "\u29B1",
  dfisht: "\u297F",
  dfr: "\u{1D521}",
  dharl: "\u21C3",
  dharr: "\u21C2",
  diam: "\u22C4",
  diamond: "\u22C4",
  diamondsuit: "\u2666",
  diams: "\u2666",
  die: "\xA8",
  digamma: "\u03DD",
  disin: "\u22F2",
  div: "\xF7",
  divide: "\xF7",
  divideontimes: "\u22C7",
  divonx: "\u22C7",
  djcy: "\u0452",
  dlcorn: "\u231E",
  dlcrop: "\u230D",
  dollar: "$",
  dopf: "\u{1D555}",
  dot: "\u02D9",
  doteq: "\u2250",
  doteqdot: "\u2251",
  dotminus: "\u2238",
  dotplus: "\u2214",
  dotsquare: "\u22A1",
  doublebarwedge: "\u2306",
  downarrow: "\u2193",
  downdownarrows: "\u21CA",
  downharpoonleft: "\u21C3",
  downharpoonright: "\u21C2",
  drbkarow: "\u2910",
  drcorn: "\u231F",
  drcrop: "\u230C",
  dscr: "\u{1D4B9}",
  dscy: "\u0455",
  dsol: "\u29F6",
  dstrok: "\u0111",
  dtdot: "\u22F1",
  dtri: "\u25BF",
  dtrif: "\u25BE",
  duarr: "\u21F5",
  duhar: "\u296F",
  dwangle: "\u29A6",
  dzcy: "\u045F",
  dzigrarr: "\u27FF",
  eDDot: "\u2A77",
  eDot: "\u2251",
  eacute: "\xE9",
  easter: "\u2A6E",
  ecaron: "\u011B",
  ecir: "\u2256",
  ecirc: "\xEA",
  ecolon: "\u2255",
  ecy: "\u044D",
  edot: "\u0117",
  ee: "\u2147",
  efDot: "\u2252",
  efr: "\u{1D522}",
  eg: "\u2A9A",
  egrave: "\xE8",
  egs: "\u2A96",
  egsdot: "\u2A98",
  el: "\u2A99",
  elinters: "\u23E7",
  ell: "\u2113",
  els: "\u2A95",
  elsdot: "\u2A97",
  emacr: "\u0113",
  empty: "\u2205",
  emptyset: "\u2205",
  emptyv: "\u2205",
  emsp13: "\u2004",
  emsp14: "\u2005",
  emsp: "\u2003",
  eng: "\u014B",
  ensp: "\u2002",
  eogon: "\u0119",
  eopf: "\u{1D556}",
  epar: "\u22D5",
  eparsl: "\u29E3",
  eplus: "\u2A71",
  epsi: "\u03B5",
  epsilon: "\u03B5",
  epsiv: "\u03F5",
  eqcirc: "\u2256",
  eqcolon: "\u2255",
  eqsim: "\u2242",
  eqslantgtr: "\u2A96",
  eqslantless: "\u2A95",
  equals: "=",
  equest: "\u225F",
  equiv: "\u2261",
  equivDD: "\u2A78",
  eqvparsl: "\u29E5",
  erDot: "\u2253",
  erarr: "\u2971",
  escr: "\u212F",
  esdot: "\u2250",
  esim: "\u2242",
  eta: "\u03B7",
  eth: "\xF0",
  euml: "\xEB",
  euro: "\u20AC",
  excl: "!",
  exist: "\u2203",
  expectation: "\u2130",
  exponentiale: "\u2147",
  fallingdotseq: "\u2252",
  fcy: "\u0444",
  female: "\u2640",
  ffilig: "\uFB03",
  fflig: "\uFB00",
  ffllig: "\uFB04",
  ffr: "\u{1D523}",
  filig: "\uFB01",
  fjlig: "fj",
  flat: "\u266D",
  fllig: "\uFB02",
  fltns: "\u25B1",
  fnof: "\u0192",
  fopf: "\u{1D557}",
  forall: "\u2200",
  fork: "\u22D4",
  forkv: "\u2AD9",
  fpartint: "\u2A0D",
  frac12: "\xBD",
  frac13: "\u2153",
  frac14: "\xBC",
  frac15: "\u2155",
  frac16: "\u2159",
  frac18: "\u215B",
  frac23: "\u2154",
  frac25: "\u2156",
  frac34: "\xBE",
  frac35: "\u2157",
  frac38: "\u215C",
  frac45: "\u2158",
  frac56: "\u215A",
  frac58: "\u215D",
  frac78: "\u215E",
  frasl: "\u2044",
  frown: "\u2322",
  fscr: "\u{1D4BB}",
  gE: "\u2267",
  gEl: "\u2A8C",
  gacute: "\u01F5",
  gamma: "\u03B3",
  gammad: "\u03DD",
  gap: "\u2A86",
  gbreve: "\u011F",
  gcirc: "\u011D",
  gcy: "\u0433",
  gdot: "\u0121",
  ge: "\u2265",
  gel: "\u22DB",
  geq: "\u2265",
  geqq: "\u2267",
  geqslant: "\u2A7E",
  ges: "\u2A7E",
  gescc: "\u2AA9",
  gesdot: "\u2A80",
  gesdoto: "\u2A82",
  gesdotol: "\u2A84",
  gesl: "\u22DB\uFE00",
  gesles: "\u2A94",
  gfr: "\u{1D524}",
  gg: "\u226B",
  ggg: "\u22D9",
  gimel: "\u2137",
  gjcy: "\u0453",
  gl: "\u2277",
  glE: "\u2A92",
  gla: "\u2AA5",
  glj: "\u2AA4",
  gnE: "\u2269",
  gnap: "\u2A8A",
  gnapprox: "\u2A8A",
  gne: "\u2A88",
  gneq: "\u2A88",
  gneqq: "\u2269",
  gnsim: "\u22E7",
  gopf: "\u{1D558}",
  grave: "`",
  gscr: "\u210A",
  gsim: "\u2273",
  gsime: "\u2A8E",
  gsiml: "\u2A90",
  gt: ">",
  gtcc: "\u2AA7",
  gtcir: "\u2A7A",
  gtdot: "\u22D7",
  gtlPar: "\u2995",
  gtquest: "\u2A7C",
  gtrapprox: "\u2A86",
  gtrarr: "\u2978",
  gtrdot: "\u22D7",
  gtreqless: "\u22DB",
  gtreqqless: "\u2A8C",
  gtrless: "\u2277",
  gtrsim: "\u2273",
  gvertneqq: "\u2269\uFE00",
  gvnE: "\u2269\uFE00",
  hArr: "\u21D4",
  hairsp: "\u200A",
  half: "\xBD",
  hamilt: "\u210B",
  hardcy: "\u044A",
  harr: "\u2194",
  harrcir: "\u2948",
  harrw: "\u21AD",
  hbar: "\u210F",
  hcirc: "\u0125",
  hearts: "\u2665",
  heartsuit: "\u2665",
  hellip: "\u2026",
  hercon: "\u22B9",
  hfr: "\u{1D525}",
  hksearow: "\u2925",
  hkswarow: "\u2926",
  hoarr: "\u21FF",
  homtht: "\u223B",
  hookleftarrow: "\u21A9",
  hookrightarrow: "\u21AA",
  hopf: "\u{1D559}",
  horbar: "\u2015",
  hscr: "\u{1D4BD}",
  hslash: "\u210F",
  hstrok: "\u0127",
  hybull: "\u2043",
  hyphen: "\u2010",
  iacute: "\xED",
  ic: "\u2063",
  icirc: "\xEE",
  icy: "\u0438",
  iecy: "\u0435",
  iexcl: "\xA1",
  iff: "\u21D4",
  ifr: "\u{1D526}",
  igrave: "\xEC",
  ii: "\u2148",
  iiiint: "\u2A0C",
  iiint: "\u222D",
  iinfin: "\u29DC",
  iiota: "\u2129",
  ijlig: "\u0133",
  imacr: "\u012B",
  image: "\u2111",
  imagline: "\u2110",
  imagpart: "\u2111",
  imath: "\u0131",
  imof: "\u22B7",
  imped: "\u01B5",
  in: "\u2208",
  incare: "\u2105",
  infin: "\u221E",
  infintie: "\u29DD",
  inodot: "\u0131",
  int: "\u222B",
  intcal: "\u22BA",
  integers: "\u2124",
  intercal: "\u22BA",
  intlarhk: "\u2A17",
  intprod: "\u2A3C",
  iocy: "\u0451",
  iogon: "\u012F",
  iopf: "\u{1D55A}",
  iota: "\u03B9",
  iprod: "\u2A3C",
  iquest: "\xBF",
  iscr: "\u{1D4BE}",
  isin: "\u2208",
  isinE: "\u22F9",
  isindot: "\u22F5",
  isins: "\u22F4",
  isinsv: "\u22F3",
  isinv: "\u2208",
  it: "\u2062",
  itilde: "\u0129",
  iukcy: "\u0456",
  iuml: "\xEF",
  jcirc: "\u0135",
  jcy: "\u0439",
  jfr: "\u{1D527}",
  jmath: "\u0237",
  jopf: "\u{1D55B}",
  jscr: "\u{1D4BF}",
  jsercy: "\u0458",
  jukcy: "\u0454",
  kappa: "\u03BA",
  kappav: "\u03F0",
  kcedil: "\u0137",
  kcy: "\u043A",
  kfr: "\u{1D528}",
  kgreen: "\u0138",
  khcy: "\u0445",
  kjcy: "\u045C",
  kopf: "\u{1D55C}",
  kscr: "\u{1D4C0}",
  lAarr: "\u21DA",
  lArr: "\u21D0",
  lAtail: "\u291B",
  lBarr: "\u290E",
  lE: "\u2266",
  lEg: "\u2A8B",
  lHar: "\u2962",
  lacute: "\u013A",
  laemptyv: "\u29B4",
  lagran: "\u2112",
  lambda: "\u03BB",
  lang: "\u27E8",
  langd: "\u2991",
  langle: "\u27E8",
  lap: "\u2A85",
  laquo: "\xAB",
  larr: "\u2190",
  larrb: "\u21E4",
  larrbfs: "\u291F",
  larrfs: "\u291D",
  larrhk: "\u21A9",
  larrlp: "\u21AB",
  larrpl: "\u2939",
  larrsim: "\u2973",
  larrtl: "\u21A2",
  lat: "\u2AAB",
  latail: "\u2919",
  late: "\u2AAD",
  lates: "\u2AAD\uFE00",
  lbarr: "\u290C",
  lbbrk: "\u2772",
  lbrace: "{",
  lbrack: "[",
  lbrke: "\u298B",
  lbrksld: "\u298F",
  lbrkslu: "\u298D",
  lcaron: "\u013E",
  lcedil: "\u013C",
  lceil: "\u2308",
  lcub: "{",
  lcy: "\u043B",
  ldca: "\u2936",
  ldquo: "\u201C",
  ldquor: "\u201E",
  ldrdhar: "\u2967",
  ldrushar: "\u294B",
  ldsh: "\u21B2",
  le: "\u2264",
  leftarrow: "\u2190",
  leftarrowtail: "\u21A2",
  leftharpoondown: "\u21BD",
  leftharpoonup: "\u21BC",
  leftleftarrows: "\u21C7",
  leftrightarrow: "\u2194",
  leftrightarrows: "\u21C6",
  leftrightharpoons: "\u21CB",
  leftrightsquigarrow: "\u21AD",
  leftthreetimes: "\u22CB",
  leg: "\u22DA",
  leq: "\u2264",
  leqq: "\u2266",
  leqslant: "\u2A7D",
  les: "\u2A7D",
  lescc: "\u2AA8",
  lesdot: "\u2A7F",
  lesdoto: "\u2A81",
  lesdotor: "\u2A83",
  lesg: "\u22DA\uFE00",
  lesges: "\u2A93",
  lessapprox: "\u2A85",
  lessdot: "\u22D6",
  lesseqgtr: "\u22DA",
  lesseqqgtr: "\u2A8B",
  lessgtr: "\u2276",
  lesssim: "\u2272",
  lfisht: "\u297C",
  lfloor: "\u230A",
  lfr: "\u{1D529}",
  lg: "\u2276",
  lgE: "\u2A91",
  lhard: "\u21BD",
  lharu: "\u21BC",
  lharul: "\u296A",
  lhblk: "\u2584",
  ljcy: "\u0459",
  ll: "\u226A",
  llarr: "\u21C7",
  llcorner: "\u231E",
  llhard: "\u296B",
  lltri: "\u25FA",
  lmidot: "\u0140",
  lmoust: "\u23B0",
  lmoustache: "\u23B0",
  lnE: "\u2268",
  lnap: "\u2A89",
  lnapprox: "\u2A89",
  lne: "\u2A87",
  lneq: "\u2A87",
  lneqq: "\u2268",
  lnsim: "\u22E6",
  loang: "\u27EC",
  loarr: "\u21FD",
  lobrk: "\u27E6",
  longleftarrow: "\u27F5",
  longleftrightarrow: "\u27F7",
  longmapsto: "\u27FC",
  longrightarrow: "\u27F6",
  looparrowleft: "\u21AB",
  looparrowright: "\u21AC",
  lopar: "\u2985",
  lopf: "\u{1D55D}",
  loplus: "\u2A2D",
  lotimes: "\u2A34",
  lowast: "\u2217",
  lowbar: "_",
  loz: "\u25CA",
  lozenge: "\u25CA",
  lozf: "\u29EB",
  lpar: "(",
  lparlt: "\u2993",
  lrarr: "\u21C6",
  lrcorner: "\u231F",
  lrhar: "\u21CB",
  lrhard: "\u296D",
  lrm: "\u200E",
  lrtri: "\u22BF",
  lsaquo: "\u2039",
  lscr: "\u{1D4C1}",
  lsh: "\u21B0",
  lsim: "\u2272",
  lsime: "\u2A8D",
  lsimg: "\u2A8F",
  lsqb: "[",
  lsquo: "\u2018",
  lsquor: "\u201A",
  lstrok: "\u0142",
  lt: "<",
  ltcc: "\u2AA6",
  ltcir: "\u2A79",
  ltdot: "\u22D6",
  lthree: "\u22CB",
  ltimes: "\u22C9",
  ltlarr: "\u2976",
  ltquest: "\u2A7B",
  ltrPar: "\u2996",
  ltri: "\u25C3",
  ltrie: "\u22B4",
  ltrif: "\u25C2",
  lurdshar: "\u294A",
  luruhar: "\u2966",
  lvertneqq: "\u2268\uFE00",
  lvnE: "\u2268\uFE00",
  mDDot: "\u223A",
  macr: "\xAF",
  male: "\u2642",
  malt: "\u2720",
  maltese: "\u2720",
  map: "\u21A6",
  mapsto: "\u21A6",
  mapstodown: "\u21A7",
  mapstoleft: "\u21A4",
  mapstoup: "\u21A5",
  marker: "\u25AE",
  mcomma: "\u2A29",
  mcy: "\u043C",
  mdash: "\u2014",
  measuredangle: "\u2221",
  mfr: "\u{1D52A}",
  mho: "\u2127",
  micro: "\xB5",
  mid: "\u2223",
  midast: "*",
  midcir: "\u2AF0",
  middot: "\xB7",
  minus: "\u2212",
  minusb: "\u229F",
  minusd: "\u2238",
  minusdu: "\u2A2A",
  mlcp: "\u2ADB",
  mldr: "\u2026",
  mnplus: "\u2213",
  models: "\u22A7",
  mopf: "\u{1D55E}",
  mp: "\u2213",
  mscr: "\u{1D4C2}",
  mstpos: "\u223E",
  mu: "\u03BC",
  multimap: "\u22B8",
  mumap: "\u22B8",
  nGg: "\u22D9\u0338",
  nGt: "\u226B\u20D2",
  nGtv: "\u226B\u0338",
  nLeftarrow: "\u21CD",
  nLeftrightarrow: "\u21CE",
  nLl: "\u22D8\u0338",
  nLt: "\u226A\u20D2",
  nLtv: "\u226A\u0338",
  nRightarrow: "\u21CF",
  nVDash: "\u22AF",
  nVdash: "\u22AE",
  nabla: "\u2207",
  nacute: "\u0144",
  nang: "\u2220\u20D2",
  nap: "\u2249",
  napE: "\u2A70\u0338",
  napid: "\u224B\u0338",
  napos: "\u0149",
  napprox: "\u2249",
  natur: "\u266E",
  natural: "\u266E",
  naturals: "\u2115",
  nbsp: "\xA0",
  nbump: "\u224E\u0338",
  nbumpe: "\u224F\u0338",
  ncap: "\u2A43",
  ncaron: "\u0148",
  ncedil: "\u0146",
  ncong: "\u2247",
  ncongdot: "\u2A6D\u0338",
  ncup: "\u2A42",
  ncy: "\u043D",
  ndash: "\u2013",
  ne: "\u2260",
  neArr: "\u21D7",
  nearhk: "\u2924",
  nearr: "\u2197",
  nearrow: "\u2197",
  nedot: "\u2250\u0338",
  nequiv: "\u2262",
  nesear: "\u2928",
  nesim: "\u2242\u0338",
  nexist: "\u2204",
  nexists: "\u2204",
  nfr: "\u{1D52B}",
  ngE: "\u2267\u0338",
  nge: "\u2271",
  ngeq: "\u2271",
  ngeqq: "\u2267\u0338",
  ngeqslant: "\u2A7E\u0338",
  nges: "\u2A7E\u0338",
  ngsim: "\u2275",
  ngt: "\u226F",
  ngtr: "\u226F",
  nhArr: "\u21CE",
  nharr: "\u21AE",
  nhpar: "\u2AF2",
  ni: "\u220B",
  nis: "\u22FC",
  nisd: "\u22FA",
  niv: "\u220B",
  njcy: "\u045A",
  nlArr: "\u21CD",
  nlE: "\u2266\u0338",
  nlarr: "\u219A",
  nldr: "\u2025",
  nle: "\u2270",
  nleftarrow: "\u219A",
  nleftrightarrow: "\u21AE",
  nleq: "\u2270",
  nleqq: "\u2266\u0338",
  nleqslant: "\u2A7D\u0338",
  nles: "\u2A7D\u0338",
  nless: "\u226E",
  nlsim: "\u2274",
  nlt: "\u226E",
  nltri: "\u22EA",
  nltrie: "\u22EC",
  nmid: "\u2224",
  nopf: "\u{1D55F}",
  not: "\xAC",
  notin: "\u2209",
  notinE: "\u22F9\u0338",
  notindot: "\u22F5\u0338",
  notinva: "\u2209",
  notinvb: "\u22F7",
  notinvc: "\u22F6",
  notni: "\u220C",
  notniva: "\u220C",
  notnivb: "\u22FE",
  notnivc: "\u22FD",
  npar: "\u2226",
  nparallel: "\u2226",
  nparsl: "\u2AFD\u20E5",
  npart: "\u2202\u0338",
  npolint: "\u2A14",
  npr: "\u2280",
  nprcue: "\u22E0",
  npre: "\u2AAF\u0338",
  nprec: "\u2280",
  npreceq: "\u2AAF\u0338",
  nrArr: "\u21CF",
  nrarr: "\u219B",
  nrarrc: "\u2933\u0338",
  nrarrw: "\u219D\u0338",
  nrightarrow: "\u219B",
  nrtri: "\u22EB",
  nrtrie: "\u22ED",
  nsc: "\u2281",
  nsccue: "\u22E1",
  nsce: "\u2AB0\u0338",
  nscr: "\u{1D4C3}",
  nshortmid: "\u2224",
  nshortparallel: "\u2226",
  nsim: "\u2241",
  nsime: "\u2244",
  nsimeq: "\u2244",
  nsmid: "\u2224",
  nspar: "\u2226",
  nsqsube: "\u22E2",
  nsqsupe: "\u22E3",
  nsub: "\u2284",
  nsubE: "\u2AC5\u0338",
  nsube: "\u2288",
  nsubset: "\u2282\u20D2",
  nsubseteq: "\u2288",
  nsubseteqq: "\u2AC5\u0338",
  nsucc: "\u2281",
  nsucceq: "\u2AB0\u0338",
  nsup: "\u2285",
  nsupE: "\u2AC6\u0338",
  nsupe: "\u2289",
  nsupset: "\u2283\u20D2",
  nsupseteq: "\u2289",
  nsupseteqq: "\u2AC6\u0338",
  ntgl: "\u2279",
  ntilde: "\xF1",
  ntlg: "\u2278",
  ntriangleleft: "\u22EA",
  ntrianglelefteq: "\u22EC",
  ntriangleright: "\u22EB",
  ntrianglerighteq: "\u22ED",
  nu: "\u03BD",
  num: "#",
  numero: "\u2116",
  numsp: "\u2007",
  nvDash: "\u22AD",
  nvHarr: "\u2904",
  nvap: "\u224D\u20D2",
  nvdash: "\u22AC",
  nvge: "\u2265\u20D2",
  nvgt: ">\u20D2",
  nvinfin: "\u29DE",
  nvlArr: "\u2902",
  nvle: "\u2264\u20D2",
  nvlt: "<\u20D2",
  nvltrie: "\u22B4\u20D2",
  nvrArr: "\u2903",
  nvrtrie: "\u22B5\u20D2",
  nvsim: "\u223C\u20D2",
  nwArr: "\u21D6",
  nwarhk: "\u2923",
  nwarr: "\u2196",
  nwarrow: "\u2196",
  nwnear: "\u2927",
  oS: "\u24C8",
  oacute: "\xF3",
  oast: "\u229B",
  ocir: "\u229A",
  ocirc: "\xF4",
  ocy: "\u043E",
  odash: "\u229D",
  odblac: "\u0151",
  odiv: "\u2A38",
  odot: "\u2299",
  odsold: "\u29BC",
  oelig: "\u0153",
  ofcir: "\u29BF",
  ofr: "\u{1D52C}",
  ogon: "\u02DB",
  ograve: "\xF2",
  ogt: "\u29C1",
  ohbar: "\u29B5",
  ohm: "\u03A9",
  oint: "\u222E",
  olarr: "\u21BA",
  olcir: "\u29BE",
  olcross: "\u29BB",
  oline: "\u203E",
  olt: "\u29C0",
  omacr: "\u014D",
  omega: "\u03C9",
  omicron: "\u03BF",
  omid: "\u29B6",
  ominus: "\u2296",
  oopf: "\u{1D560}",
  opar: "\u29B7",
  operp: "\u29B9",
  oplus: "\u2295",
  or: "\u2228",
  orarr: "\u21BB",
  ord: "\u2A5D",
  order: "\u2134",
  orderof: "\u2134",
  ordf: "\xAA",
  ordm: "\xBA",
  origof: "\u22B6",
  oror: "\u2A56",
  orslope: "\u2A57",
  orv: "\u2A5B",
  oscr: "\u2134",
  oslash: "\xF8",
  osol: "\u2298",
  otilde: "\xF5",
  otimes: "\u2297",
  otimesas: "\u2A36",
  ouml: "\xF6",
  ovbar: "\u233D",
  par: "\u2225",
  para: "\xB6",
  parallel: "\u2225",
  parsim: "\u2AF3",
  parsl: "\u2AFD",
  part: "\u2202",
  pcy: "\u043F",
  percnt: "%",
  period: ".",
  permil: "\u2030",
  perp: "\u22A5",
  pertenk: "\u2031",
  pfr: "\u{1D52D}",
  phi: "\u03C6",
  phiv: "\u03D5",
  phmmat: "\u2133",
  phone: "\u260E",
  pi: "\u03C0",
  pitchfork: "\u22D4",
  piv: "\u03D6",
  planck: "\u210F",
  planckh: "\u210E",
  plankv: "\u210F",
  plus: "+",
  plusacir: "\u2A23",
  plusb: "\u229E",
  pluscir: "\u2A22",
  plusdo: "\u2214",
  plusdu: "\u2A25",
  pluse: "\u2A72",
  plusmn: "\xB1",
  plussim: "\u2A26",
  plustwo: "\u2A27",
  pm: "\xB1",
  pointint: "\u2A15",
  popf: "\u{1D561}",
  pound: "\xA3",
  pr: "\u227A",
  prE: "\u2AB3",
  prap: "\u2AB7",
  prcue: "\u227C",
  pre: "\u2AAF",
  prec: "\u227A",
  precapprox: "\u2AB7",
  preccurlyeq: "\u227C",
  preceq: "\u2AAF",
  precnapprox: "\u2AB9",
  precneqq: "\u2AB5",
  precnsim: "\u22E8",
  precsim: "\u227E",
  prime: "\u2032",
  primes: "\u2119",
  prnE: "\u2AB5",
  prnap: "\u2AB9",
  prnsim: "\u22E8",
  prod: "\u220F",
  profalar: "\u232E",
  profline: "\u2312",
  profsurf: "\u2313",
  prop: "\u221D",
  propto: "\u221D",
  prsim: "\u227E",
  prurel: "\u22B0",
  pscr: "\u{1D4C5}",
  psi: "\u03C8",
  puncsp: "\u2008",
  qfr: "\u{1D52E}",
  qint: "\u2A0C",
  qopf: "\u{1D562}",
  qprime: "\u2057",
  qscr: "\u{1D4C6}",
  quaternions: "\u210D",
  quatint: "\u2A16",
  quest: "?",
  questeq: "\u225F",
  quot: '"',
  rAarr: "\u21DB",
  rArr: "\u21D2",
  rAtail: "\u291C",
  rBarr: "\u290F",
  rHar: "\u2964",
  race: "\u223D\u0331",
  racute: "\u0155",
  radic: "\u221A",
  raemptyv: "\u29B3",
  rang: "\u27E9",
  rangd: "\u2992",
  range: "\u29A5",
  rangle: "\u27E9",
  raquo: "\xBB",
  rarr: "\u2192",
  rarrap: "\u2975",
  rarrb: "\u21E5",
  rarrbfs: "\u2920",
  rarrc: "\u2933",
  rarrfs: "\u291E",
  rarrhk: "\u21AA",
  rarrlp: "\u21AC",
  rarrpl: "\u2945",
  rarrsim: "\u2974",
  rarrtl: "\u21A3",
  rarrw: "\u219D",
  ratail: "\u291A",
  ratio: "\u2236",
  rationals: "\u211A",
  rbarr: "\u290D",
  rbbrk: "\u2773",
  rbrace: "}",
  rbrack: "]",
  rbrke: "\u298C",
  rbrksld: "\u298E",
  rbrkslu: "\u2990",
  rcaron: "\u0159",
  rcedil: "\u0157",
  rceil: "\u2309",
  rcub: "}",
  rcy: "\u0440",
  rdca: "\u2937",
  rdldhar: "\u2969",
  rdquo: "\u201D",
  rdquor: "\u201D",
  rdsh: "\u21B3",
  real: "\u211C",
  realine: "\u211B",
  realpart: "\u211C",
  reals: "\u211D",
  rect: "\u25AD",
  reg: "\xAE",
  rfisht: "\u297D",
  rfloor: "\u230B",
  rfr: "\u{1D52F}",
  rhard: "\u21C1",
  rharu: "\u21C0",
  rharul: "\u296C",
  rho: "\u03C1",
  rhov: "\u03F1",
  rightarrow: "\u2192",
  rightarrowtail: "\u21A3",
  rightharpoondown: "\u21C1",
  rightharpoonup: "\u21C0",
  rightleftarrows: "\u21C4",
  rightleftharpoons: "\u21CC",
  rightrightarrows: "\u21C9",
  rightsquigarrow: "\u219D",
  rightthreetimes: "\u22CC",
  ring: "\u02DA",
  risingdotseq: "\u2253",
  rlarr: "\u21C4",
  rlhar: "\u21CC",
  rlm: "\u200F",
  rmoust: "\u23B1",
  rmoustache: "\u23B1",
  rnmid: "\u2AEE",
  roang: "\u27ED",
  roarr: "\u21FE",
  robrk: "\u27E7",
  ropar: "\u2986",
  ropf: "\u{1D563}",
  roplus: "\u2A2E",
  rotimes: "\u2A35",
  rpar: ")",
  rpargt: "\u2994",
  rppolint: "\u2A12",
  rrarr: "\u21C9",
  rsaquo: "\u203A",
  rscr: "\u{1D4C7}",
  rsh: "\u21B1",
  rsqb: "]",
  rsquo: "\u2019",
  rsquor: "\u2019",
  rthree: "\u22CC",
  rtimes: "\u22CA",
  rtri: "\u25B9",
  rtrie: "\u22B5",
  rtrif: "\u25B8",
  rtriltri: "\u29CE",
  ruluhar: "\u2968",
  rx: "\u211E",
  sacute: "\u015B",
  sbquo: "\u201A",
  sc: "\u227B",
  scE: "\u2AB4",
  scap: "\u2AB8",
  scaron: "\u0161",
  sccue: "\u227D",
  sce: "\u2AB0",
  scedil: "\u015F",
  scirc: "\u015D",
  scnE: "\u2AB6",
  scnap: "\u2ABA",
  scnsim: "\u22E9",
  scpolint: "\u2A13",
  scsim: "\u227F",
  scy: "\u0441",
  sdot: "\u22C5",
  sdotb: "\u22A1",
  sdote: "\u2A66",
  seArr: "\u21D8",
  searhk: "\u2925",
  searr: "\u2198",
  searrow: "\u2198",
  sect: "\xA7",
  semi: ";",
  seswar: "\u2929",
  setminus: "\u2216",
  setmn: "\u2216",
  sext: "\u2736",
  sfr: "\u{1D530}",
  sfrown: "\u2322",
  sharp: "\u266F",
  shchcy: "\u0449",
  shcy: "\u0448",
  shortmid: "\u2223",
  shortparallel: "\u2225",
  shy: "\xAD",
  sigma: "\u03C3",
  sigmaf: "\u03C2",
  sigmav: "\u03C2",
  sim: "\u223C",
  simdot: "\u2A6A",
  sime: "\u2243",
  simeq: "\u2243",
  simg: "\u2A9E",
  simgE: "\u2AA0",
  siml: "\u2A9D",
  simlE: "\u2A9F",
  simne: "\u2246",
  simplus: "\u2A24",
  simrarr: "\u2972",
  slarr: "\u2190",
  smallsetminus: "\u2216",
  smashp: "\u2A33",
  smeparsl: "\u29E4",
  smid: "\u2223",
  smile: "\u2323",
  smt: "\u2AAA",
  smte: "\u2AAC",
  smtes: "\u2AAC\uFE00",
  softcy: "\u044C",
  sol: "/",
  solb: "\u29C4",
  solbar: "\u233F",
  sopf: "\u{1D564}",
  spades: "\u2660",
  spadesuit: "\u2660",
  spar: "\u2225",
  sqcap: "\u2293",
  sqcaps: "\u2293\uFE00",
  sqcup: "\u2294",
  sqcups: "\u2294\uFE00",
  sqsub: "\u228F",
  sqsube: "\u2291",
  sqsubset: "\u228F",
  sqsubseteq: "\u2291",
  sqsup: "\u2290",
  sqsupe: "\u2292",
  sqsupset: "\u2290",
  sqsupseteq: "\u2292",
  squ: "\u25A1",
  square: "\u25A1",
  squarf: "\u25AA",
  squf: "\u25AA",
  srarr: "\u2192",
  sscr: "\u{1D4C8}",
  ssetmn: "\u2216",
  ssmile: "\u2323",
  sstarf: "\u22C6",
  star: "\u2606",
  starf: "\u2605",
  straightepsilon: "\u03F5",
  straightphi: "\u03D5",
  strns: "\xAF",
  sub: "\u2282",
  subE: "\u2AC5",
  subdot: "\u2ABD",
  sube: "\u2286",
  subedot: "\u2AC3",
  submult: "\u2AC1",
  subnE: "\u2ACB",
  subne: "\u228A",
  subplus: "\u2ABF",
  subrarr: "\u2979",
  subset: "\u2282",
  subseteq: "\u2286",
  subseteqq: "\u2AC5",
  subsetneq: "\u228A",
  subsetneqq: "\u2ACB",
  subsim: "\u2AC7",
  subsub: "\u2AD5",
  subsup: "\u2AD3",
  succ: "\u227B",
  succapprox: "\u2AB8",
  succcurlyeq: "\u227D",
  succeq: "\u2AB0",
  succnapprox: "\u2ABA",
  succneqq: "\u2AB6",
  succnsim: "\u22E9",
  succsim: "\u227F",
  sum: "\u2211",
  sung: "\u266A",
  sup1: "\xB9",
  sup2: "\xB2",
  sup3: "\xB3",
  sup: "\u2283",
  supE: "\u2AC6",
  supdot: "\u2ABE",
  supdsub: "\u2AD8",
  supe: "\u2287",
  supedot: "\u2AC4",
  suphsol: "\u27C9",
  suphsub: "\u2AD7",
  suplarr: "\u297B",
  supmult: "\u2AC2",
  supnE: "\u2ACC",
  supne: "\u228B",
  supplus: "\u2AC0",
  supset: "\u2283",
  supseteq: "\u2287",
  supseteqq: "\u2AC6",
  supsetneq: "\u228B",
  supsetneqq: "\u2ACC",
  supsim: "\u2AC8",
  supsub: "\u2AD4",
  supsup: "\u2AD6",
  swArr: "\u21D9",
  swarhk: "\u2926",
  swarr: "\u2199",
  swarrow: "\u2199",
  swnwar: "\u292A",
  szlig: "\xDF",
  target: "\u2316",
  tau: "\u03C4",
  tbrk: "\u23B4",
  tcaron: "\u0165",
  tcedil: "\u0163",
  tcy: "\u0442",
  tdot: "\u20DB",
  telrec: "\u2315",
  tfr: "\u{1D531}",
  there4: "\u2234",
  therefore: "\u2234",
  theta: "\u03B8",
  thetasym: "\u03D1",
  thetav: "\u03D1",
  thickapprox: "\u2248",
  thicksim: "\u223C",
  thinsp: "\u2009",
  thkap: "\u2248",
  thksim: "\u223C",
  thorn: "\xFE",
  tilde: "\u02DC",
  times: "\xD7",
  timesb: "\u22A0",
  timesbar: "\u2A31",
  timesd: "\u2A30",
  tint: "\u222D",
  toea: "\u2928",
  top: "\u22A4",
  topbot: "\u2336",
  topcir: "\u2AF1",
  topf: "\u{1D565}",
  topfork: "\u2ADA",
  tosa: "\u2929",
  tprime: "\u2034",
  trade: "\u2122",
  triangle: "\u25B5",
  triangledown: "\u25BF",
  triangleleft: "\u25C3",
  trianglelefteq: "\u22B4",
  triangleq: "\u225C",
  triangleright: "\u25B9",
  trianglerighteq: "\u22B5",
  tridot: "\u25EC",
  trie: "\u225C",
  triminus: "\u2A3A",
  triplus: "\u2A39",
  trisb: "\u29CD",
  tritime: "\u2A3B",
  trpezium: "\u23E2",
  tscr: "\u{1D4C9}",
  tscy: "\u0446",
  tshcy: "\u045B",
  tstrok: "\u0167",
  twixt: "\u226C",
  twoheadleftarrow: "\u219E",
  twoheadrightarrow: "\u21A0",
  uArr: "\u21D1",
  uHar: "\u2963",
  uacute: "\xFA",
  uarr: "\u2191",
  ubrcy: "\u045E",
  ubreve: "\u016D",
  ucirc: "\xFB",
  ucy: "\u0443",
  udarr: "\u21C5",
  udblac: "\u0171",
  udhar: "\u296E",
  ufisht: "\u297E",
  ufr: "\u{1D532}",
  ugrave: "\xF9",
  uharl: "\u21BF",
  uharr: "\u21BE",
  uhblk: "\u2580",
  ulcorn: "\u231C",
  ulcorner: "\u231C",
  ulcrop: "\u230F",
  ultri: "\u25F8",
  umacr: "\u016B",
  uml: "\xA8",
  uogon: "\u0173",
  uopf: "\u{1D566}",
  uparrow: "\u2191",
  updownarrow: "\u2195",
  upharpoonleft: "\u21BF",
  upharpoonright: "\u21BE",
  uplus: "\u228E",
  upsi: "\u03C5",
  upsih: "\u03D2",
  upsilon: "\u03C5",
  upuparrows: "\u21C8",
  urcorn: "\u231D",
  urcorner: "\u231D",
  urcrop: "\u230E",
  uring: "\u016F",
  urtri: "\u25F9",
  uscr: "\u{1D4CA}",
  utdot: "\u22F0",
  utilde: "\u0169",
  utri: "\u25B5",
  utrif: "\u25B4",
  uuarr: "\u21C8",
  uuml: "\xFC",
  uwangle: "\u29A7",
  vArr: "\u21D5",
  vBar: "\u2AE8",
  vBarv: "\u2AE9",
  vDash: "\u22A8",
  vangrt: "\u299C",
  varepsilon: "\u03F5",
  varkappa: "\u03F0",
  varnothing: "\u2205",
  varphi: "\u03D5",
  varpi: "\u03D6",
  varpropto: "\u221D",
  varr: "\u2195",
  varrho: "\u03F1",
  varsigma: "\u03C2",
  varsubsetneq: "\u228A\uFE00",
  varsubsetneqq: "\u2ACB\uFE00",
  varsupsetneq: "\u228B\uFE00",
  varsupsetneqq: "\u2ACC\uFE00",
  vartheta: "\u03D1",
  vartriangleleft: "\u22B2",
  vartriangleright: "\u22B3",
  vcy: "\u0432",
  vdash: "\u22A2",
  vee: "\u2228",
  veebar: "\u22BB",
  veeeq: "\u225A",
  vellip: "\u22EE",
  verbar: "|",
  vert: "|",
  vfr: "\u{1D533}",
  vltri: "\u22B2",
  vnsub: "\u2282\u20D2",
  vnsup: "\u2283\u20D2",
  vopf: "\u{1D567}",
  vprop: "\u221D",
  vrtri: "\u22B3",
  vscr: "\u{1D4CB}",
  vsubnE: "\u2ACB\uFE00",
  vsubne: "\u228A\uFE00",
  vsupnE: "\u2ACC\uFE00",
  vsupne: "\u228B\uFE00",
  vzigzag: "\u299A",
  wcirc: "\u0175",
  wedbar: "\u2A5F",
  wedge: "\u2227",
  wedgeq: "\u2259",
  weierp: "\u2118",
  wfr: "\u{1D534}",
  wopf: "\u{1D568}",
  wp: "\u2118",
  wr: "\u2240",
  wreath: "\u2240",
  wscr: "\u{1D4CC}",
  xcap: "\u22C2",
  xcirc: "\u25EF",
  xcup: "\u22C3",
  xdtri: "\u25BD",
  xfr: "\u{1D535}",
  xhArr: "\u27FA",
  xharr: "\u27F7",
  xi: "\u03BE",
  xlArr: "\u27F8",
  xlarr: "\u27F5",
  xmap: "\u27FC",
  xnis: "\u22FB",
  xodot: "\u2A00",
  xopf: "\u{1D569}",
  xoplus: "\u2A01",
  xotime: "\u2A02",
  xrArr: "\u27F9",
  xrarr: "\u27F6",
  xscr: "\u{1D4CD}",
  xsqcup: "\u2A06",
  xuplus: "\u2A04",
  xutri: "\u25B3",
  xvee: "\u22C1",
  xwedge: "\u22C0",
  yacute: "\xFD",
  yacy: "\u044F",
  ycirc: "\u0177",
  ycy: "\u044B",
  yen: "\xA5",
  yfr: "\u{1D536}",
  yicy: "\u0457",
  yopf: "\u{1D56A}",
  yscr: "\u{1D4CE}",
  yucy: "\u044E",
  yuml: "\xFF",
  zacute: "\u017A",
  zcaron: "\u017E",
  zcy: "\u0437",
  zdot: "\u017C",
  zeetrf: "\u2128",
  zeta: "\u03B6",
  zfr: "\u{1D537}",
  zhcy: "\u0436",
  zigrarr: "\u21DD",
  zopf: "\u{1D56B}",
  zscr: "\u{1D4CF}",
  zwj: "\u200D",
  zwnj: "\u200C"
};

// node_modules/decode-named-character-reference/index.js
var own = {}.hasOwnProperty;
function decodeNamedCharacterReference(value) {
  return own.call(characterEntities, value) ? characterEntities[value] : false;
}

// node_modules/micromark-util-chunked/index.js
function splice(list2, start, remove, items) {
  const end = list2.length;
  let chunkStart = 0;
  let parameters;
  if (start < 0) {
    start = -start > end ? 0 : end + start;
  } else {
    start = start > end ? end : start;
  }
  remove = remove > 0 ? remove : 0;
  if (items.length < 1e4) {
    parameters = Array.from(items);
    parameters.unshift(start, remove);
    list2.splice(...parameters);
  } else {
    if (remove) list2.splice(start, remove);
    while (chunkStart < items.length) {
      parameters = items.slice(chunkStart, chunkStart + 1e4);
      parameters.unshift(start, 0);
      list2.splice(...parameters);
      chunkStart += 1e4;
      start += 1e4;
    }
  }
}
function push(list2, items) {
  if (list2.length > 0) {
    splice(list2, list2.length, 0, items);
    return list2;
  }
  return items;
}

// node_modules/micromark-util-combine-extensions/index.js
var hasOwnProperty = {}.hasOwnProperty;
function combineExtensions(extensions) {
  const all2 = {};
  let index2 = -1;
  while (++index2 < extensions.length) {
    syntaxExtension(all2, extensions[index2]);
  }
  return all2;
}
function syntaxExtension(all2, extension2) {
  let hook;
  for (hook in extension2) {
    const maybe = hasOwnProperty.call(all2, hook) ? all2[hook] : void 0;
    const left = maybe || (all2[hook] = {});
    const right = extension2[hook];
    let code;
    if (right) {
      for (code in right) {
        if (!hasOwnProperty.call(left, code)) left[code] = [];
        const value = right[code];
        constructs(
          // @ts-expect-error Looks like a list.
          left[code],
          Array.isArray(value) ? value : value ? [value] : []
        );
      }
    }
  }
}
function constructs(existing, list2) {
  let index2 = -1;
  const before = [];
  while (++index2 < list2.length) {
    ;
    (list2[index2].add === "after" ? existing : before).push(list2[index2]);
  }
  splice(existing, 0, 0, before);
}

// node_modules/micromark-util-decode-numeric-character-reference/index.js
function decodeNumericCharacterReference(value, base) {
  const code = Number.parseInt(value, base);
  if (
    // C0 except for HT, LF, FF, CR, space.
    code < 9 || code === 11 || code > 13 && code < 32 || // Control character (DEL) of C0, and C1 controls.
    code > 126 && code < 160 || // Lone high surrogates and low surrogates.
    code > 55295 && code < 57344 || // Noncharacters.
    code > 64975 && code < 65008 || /* eslint-disable no-bitwise */
    (code & 65535) === 65535 || (code & 65535) === 65534 || /* eslint-enable no-bitwise */
    // Out of range
    code > 1114111
  ) {
    return "\uFFFD";
  }
  return String.fromCodePoint(code);
}

// node_modules/micromark-util-normalize-identifier/index.js
function normalizeIdentifier(value) {
  return value.replace(/[\t\n\r ]+/g, " ").replace(/^ | $/g, "").toLowerCase().toUpperCase();
}

// node_modules/micromark-util-character/index.js
var asciiAlpha = regexCheck(/[A-Za-z]/);
var asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);
var asciiAtext = regexCheck(/[#-'*+\--9=?A-Z^-~]/);
function asciiControl(code) {
  return (
    // Special whitespace codes (which have negative values), C0 and Control
    // character DEL
    code !== null && (code < 32 || code === 127)
  );
}
var asciiDigit = regexCheck(/\d/);
var asciiHexDigit = regexCheck(/[\dA-Fa-f]/);
var asciiPunctuation = regexCheck(/[!-/:-@[-`{-~]/);
function markdownLineEnding(code) {
  return code !== null && code < -2;
}
function markdownLineEndingOrSpace(code) {
  return code !== null && (code < 0 || code === 32);
}
function markdownSpace(code) {
  return code === -2 || code === -1 || code === 32;
}
var unicodePunctuation = regexCheck(/\p{P}|\p{S}/u);
var unicodeWhitespace = regexCheck(/\s/);
function regexCheck(regex) {
  return check;
  function check(code) {
    return code !== null && code > -1 && regex.test(String.fromCharCode(code));
  }
}

// node_modules/micromark-factory-space/index.js
function factorySpace(effects, ok2, type, max) {
  const limit = max ? max - 1 : Number.POSITIVE_INFINITY;
  let size = 0;
  return start;
  function start(code) {
    if (markdownSpace(code)) {
      effects.enter(type);
      return prefix(code);
    }
    return ok2(code);
  }
  function prefix(code) {
    if (markdownSpace(code) && size++ < limit) {
      effects.consume(code);
      return prefix;
    }
    effects.exit(type);
    return ok2(code);
  }
}

// node_modules/micromark/lib/initialize/content.js
var content = {
  tokenize: initializeContent
};
function initializeContent(effects) {
  const contentStart = effects.attempt(this.parser.constructs.contentInitial, afterContentStartConstruct, paragraphInitial);
  let previous2;
  return contentStart;
  function afterContentStartConstruct(code) {
    if (code === null) {
      effects.consume(code);
      return;
    }
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return factorySpace(effects, contentStart, "linePrefix");
  }
  function paragraphInitial(code) {
    effects.enter("paragraph");
    return lineStart(code);
  }
  function lineStart(code) {
    const token = effects.enter("chunkText", {
      contentType: "text",
      previous: previous2
    });
    if (previous2) {
      previous2.next = token;
    }
    previous2 = token;
    return data(code);
  }
  function data(code) {
    if (code === null) {
      effects.exit("chunkText");
      effects.exit("paragraph");
      effects.consume(code);
      return;
    }
    if (markdownLineEnding(code)) {
      effects.consume(code);
      effects.exit("chunkText");
      return lineStart;
    }
    effects.consume(code);
    return data;
  }
}

// node_modules/micromark/lib/initialize/document.js
var document = {
  tokenize: initializeDocument
};
var containerConstruct = {
  tokenize: tokenizeContainer
};
function initializeDocument(effects) {
  const self = this;
  const stack = [];
  let continued = 0;
  let childFlow;
  let childToken;
  let lineStartOffset;
  return start;
  function start(code) {
    if (continued < stack.length) {
      const item = stack[continued];
      self.containerState = item[1];
      return effects.attempt(item[0].continuation, documentContinue, checkNewContainers)(code);
    }
    return checkNewContainers(code);
  }
  function documentContinue(code) {
    continued++;
    if (self.containerState._closeFlow) {
      self.containerState._closeFlow = void 0;
      if (childFlow) {
        closeFlow();
      }
      const indexBeforeExits = self.events.length;
      let indexBeforeFlow = indexBeforeExits;
      let point3;
      while (indexBeforeFlow--) {
        if (self.events[indexBeforeFlow][0] === "exit" && self.events[indexBeforeFlow][1].type === "chunkFlow") {
          point3 = self.events[indexBeforeFlow][1].end;
          break;
        }
      }
      exitContainers(continued);
      let index2 = indexBeforeExits;
      while (index2 < self.events.length) {
        self.events[index2][1].end = {
          ...point3
        };
        index2++;
      }
      splice(self.events, indexBeforeFlow + 1, 0, self.events.slice(indexBeforeExits));
      self.events.length = index2;
      return checkNewContainers(code);
    }
    return start(code);
  }
  function checkNewContainers(code) {
    if (continued === stack.length) {
      if (!childFlow) {
        return documentContinued(code);
      }
      if (childFlow.currentConstruct && childFlow.currentConstruct.concrete) {
        return flowStart(code);
      }
      self.interrupt = Boolean(childFlow.currentConstruct && !childFlow._gfmTableDynamicInterruptHack);
    }
    self.containerState = {};
    return effects.check(containerConstruct, thereIsANewContainer, thereIsNoNewContainer)(code);
  }
  function thereIsANewContainer(code) {
    if (childFlow) closeFlow();
    exitContainers(continued);
    return documentContinued(code);
  }
  function thereIsNoNewContainer(code) {
    self.parser.lazy[self.now().line] = continued !== stack.length;
    lineStartOffset = self.now().offset;
    return flowStart(code);
  }
  function documentContinued(code) {
    self.containerState = {};
    return effects.attempt(containerConstruct, containerContinue, flowStart)(code);
  }
  function containerContinue(code) {
    continued++;
    stack.push([self.currentConstruct, self.containerState]);
    return documentContinued(code);
  }
  function flowStart(code) {
    if (code === null) {
      if (childFlow) closeFlow();
      exitContainers(0);
      effects.consume(code);
      return;
    }
    childFlow = childFlow || self.parser.flow(self.now());
    effects.enter("chunkFlow", {
      _tokenizer: childFlow,
      contentType: "flow",
      previous: childToken
    });
    return flowContinue(code);
  }
  function flowContinue(code) {
    if (code === null) {
      writeToChild(effects.exit("chunkFlow"), true);
      exitContainers(0);
      effects.consume(code);
      return;
    }
    if (markdownLineEnding(code)) {
      effects.consume(code);
      writeToChild(effects.exit("chunkFlow"));
      continued = 0;
      self.interrupt = void 0;
      return start;
    }
    effects.consume(code);
    return flowContinue;
  }
  function writeToChild(token, endOfFile) {
    const stream = self.sliceStream(token);
    if (endOfFile) stream.push(null);
    token.previous = childToken;
    if (childToken) childToken.next = token;
    childToken = token;
    childFlow.defineSkip(token.start);
    childFlow.write(stream);
    if (self.parser.lazy[token.start.line]) {
      let index2 = childFlow.events.length;
      while (index2--) {
        if (
          // The token starts before the line ending…
          childFlow.events[index2][1].start.offset < lineStartOffset && // …and either is not ended yet…
          (!childFlow.events[index2][1].end || // …or ends after it.
          childFlow.events[index2][1].end.offset > lineStartOffset)
        ) {
          return;
        }
      }
      const indexBeforeExits = self.events.length;
      let indexBeforeFlow = indexBeforeExits;
      let seen;
      let point3;
      while (indexBeforeFlow--) {
        if (self.events[indexBeforeFlow][0] === "exit" && self.events[indexBeforeFlow][1].type === "chunkFlow") {
          if (seen) {
            point3 = self.events[indexBeforeFlow][1].end;
            break;
          }
          seen = true;
        }
      }
      exitContainers(continued);
      index2 = indexBeforeExits;
      while (index2 < self.events.length) {
        self.events[index2][1].end = {
          ...point3
        };
        index2++;
      }
      splice(self.events, indexBeforeFlow + 1, 0, self.events.slice(indexBeforeExits));
      self.events.length = index2;
    }
  }
  function exitContainers(size) {
    let index2 = stack.length;
    while (index2-- > size) {
      const entry = stack[index2];
      self.containerState = entry[1];
      entry[0].exit.call(self, effects);
    }
    stack.length = size;
  }
  function closeFlow() {
    childFlow.write([null]);
    childToken = void 0;
    childFlow = void 0;
    self.containerState._closeFlow = void 0;
  }
}
function tokenizeContainer(effects, ok2, nok) {
  return factorySpace(effects, effects.attempt(this.parser.constructs.document, ok2, nok), "linePrefix", this.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4);
}

// node_modules/micromark-util-classify-character/index.js
function classifyCharacter(code) {
  if (code === null || markdownLineEndingOrSpace(code) || unicodeWhitespace(code)) {
    return 1;
  }
  if (unicodePunctuation(code)) {
    return 2;
  }
}

// node_modules/micromark-util-resolve-all/index.js
function resolveAll(constructs2, events, context) {
  const called = [];
  let index2 = -1;
  while (++index2 < constructs2.length) {
    const resolve15 = constructs2[index2].resolveAll;
    if (resolve15 && !called.includes(resolve15)) {
      events = resolve15(events, context);
      called.push(resolve15);
    }
  }
  return events;
}

// node_modules/micromark-core-commonmark/lib/attention.js
var attention = {
  name: "attention",
  resolveAll: resolveAllAttention,
  tokenize: tokenizeAttention
};
function resolveAllAttention(events, context) {
  let index2 = -1;
  let open5;
  let group;
  let text3;
  let openingSequence;
  let closingSequence;
  let use;
  let nextEvents;
  let offset;
  while (++index2 < events.length) {
    if (events[index2][0] === "enter" && events[index2][1].type === "attentionSequence" && events[index2][1]._close) {
      open5 = index2;
      while (open5--) {
        if (events[open5][0] === "exit" && events[open5][1].type === "attentionSequence" && events[open5][1]._open && // If the markers are the same:
        context.sliceSerialize(events[open5][1]).charCodeAt(0) === context.sliceSerialize(events[index2][1]).charCodeAt(0)) {
          if ((events[open5][1]._close || events[index2][1]._open) && (events[index2][1].end.offset - events[index2][1].start.offset) % 3 && !((events[open5][1].end.offset - events[open5][1].start.offset + events[index2][1].end.offset - events[index2][1].start.offset) % 3)) {
            continue;
          }
          use = events[open5][1].end.offset - events[open5][1].start.offset > 1 && events[index2][1].end.offset - events[index2][1].start.offset > 1 ? 2 : 1;
          const start = {
            ...events[open5][1].end
          };
          const end = {
            ...events[index2][1].start
          };
          movePoint(start, -use);
          movePoint(end, use);
          openingSequence = {
            type: use > 1 ? "strongSequence" : "emphasisSequence",
            start,
            end: {
              ...events[open5][1].end
            }
          };
          closingSequence = {
            type: use > 1 ? "strongSequence" : "emphasisSequence",
            start: {
              ...events[index2][1].start
            },
            end
          };
          text3 = {
            type: use > 1 ? "strongText" : "emphasisText",
            start: {
              ...events[open5][1].end
            },
            end: {
              ...events[index2][1].start
            }
          };
          group = {
            type: use > 1 ? "strong" : "emphasis",
            start: {
              ...openingSequence.start
            },
            end: {
              ...closingSequence.end
            }
          };
          events[open5][1].end = {
            ...openingSequence.start
          };
          events[index2][1].start = {
            ...closingSequence.end
          };
          nextEvents = [];
          if (events[open5][1].end.offset - events[open5][1].start.offset) {
            nextEvents = push(nextEvents, [["enter", events[open5][1], context], ["exit", events[open5][1], context]]);
          }
          nextEvents = push(nextEvents, [["enter", group, context], ["enter", openingSequence, context], ["exit", openingSequence, context], ["enter", text3, context]]);
          nextEvents = push(nextEvents, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open5 + 1, index2), context));
          nextEvents = push(nextEvents, [["exit", text3, context], ["enter", closingSequence, context], ["exit", closingSequence, context], ["exit", group, context]]);
          if (events[index2][1].end.offset - events[index2][1].start.offset) {
            offset = 2;
            nextEvents = push(nextEvents, [["enter", events[index2][1], context], ["exit", events[index2][1], context]]);
          } else {
            offset = 0;
          }
          splice(events, open5 - 1, index2 - open5 + 3, nextEvents);
          index2 = open5 + nextEvents.length - offset - 2;
          break;
        }
      }
    }
  }
  index2 = -1;
  while (++index2 < events.length) {
    if (events[index2][1].type === "attentionSequence") {
      events[index2][1].type = "data";
    }
  }
  return events;
}
function tokenizeAttention(effects, ok2) {
  const attentionMarkers2 = this.parser.constructs.attentionMarkers.null;
  const previous2 = this.previous;
  const before = classifyCharacter(previous2);
  let marker;
  return start;
  function start(code) {
    marker = code;
    effects.enter("attentionSequence");
    return inside(code);
  }
  function inside(code) {
    if (code === marker) {
      effects.consume(code);
      return inside;
    }
    const token = effects.exit("attentionSequence");
    const after = classifyCharacter(code);
    const open5 = !after || after === 2 && before || attentionMarkers2.includes(code);
    const close = !before || before === 2 && after || attentionMarkers2.includes(previous2);
    token._open = Boolean(marker === 42 ? open5 : open5 && (before || !close));
    token._close = Boolean(marker === 42 ? close : close && (after || !open5));
    return ok2(code);
  }
}
function movePoint(point3, offset) {
  point3.column += offset;
  point3.offset += offset;
  point3._bufferIndex += offset;
}

// node_modules/micromark-core-commonmark/lib/autolink.js
var autolink = {
  name: "autolink",
  tokenize: tokenizeAutolink
};
function tokenizeAutolink(effects, ok2, nok) {
  let size = 0;
  return start;
  function start(code) {
    effects.enter("autolink");
    effects.enter("autolinkMarker");
    effects.consume(code);
    effects.exit("autolinkMarker");
    effects.enter("autolinkProtocol");
    return open5;
  }
  function open5(code) {
    if (asciiAlpha(code)) {
      effects.consume(code);
      return schemeOrEmailAtext;
    }
    if (code === 64) {
      return nok(code);
    }
    return emailAtext(code);
  }
  function schemeOrEmailAtext(code) {
    if (code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code)) {
      size = 1;
      return schemeInsideOrEmailAtext(code);
    }
    return emailAtext(code);
  }
  function schemeInsideOrEmailAtext(code) {
    if (code === 58) {
      effects.consume(code);
      size = 0;
      return urlInside;
    }
    if ((code === 43 || code === 45 || code === 46 || asciiAlphanumeric(code)) && size++ < 32) {
      effects.consume(code);
      return schemeInsideOrEmailAtext;
    }
    size = 0;
    return emailAtext(code);
  }
  function urlInside(code) {
    if (code === 62) {
      effects.exit("autolinkProtocol");
      effects.enter("autolinkMarker");
      effects.consume(code);
      effects.exit("autolinkMarker");
      effects.exit("autolink");
      return ok2;
    }
    if (code === null || code === 32 || code === 60 || asciiControl(code)) {
      return nok(code);
    }
    effects.consume(code);
    return urlInside;
  }
  function emailAtext(code) {
    if (code === 64) {
      effects.consume(code);
      return emailAtSignOrDot;
    }
    if (asciiAtext(code)) {
      effects.consume(code);
      return emailAtext;
    }
    return nok(code);
  }
  function emailAtSignOrDot(code) {
    return asciiAlphanumeric(code) ? emailLabel(code) : nok(code);
  }
  function emailLabel(code) {
    if (code === 46) {
      effects.consume(code);
      size = 0;
      return emailAtSignOrDot;
    }
    if (code === 62) {
      effects.exit("autolinkProtocol").type = "autolinkEmail";
      effects.enter("autolinkMarker");
      effects.consume(code);
      effects.exit("autolinkMarker");
      effects.exit("autolink");
      return ok2;
    }
    return emailValue(code);
  }
  function emailValue(code) {
    if ((code === 45 || asciiAlphanumeric(code)) && size++ < 63) {
      const next = code === 45 ? emailValue : emailLabel;
      effects.consume(code);
      return next;
    }
    return nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/blank-line.js
var blankLine = {
  partial: true,
  tokenize: tokenizeBlankLine
};
function tokenizeBlankLine(effects, ok2, nok) {
  return start;
  function start(code) {
    return markdownSpace(code) ? factorySpace(effects, after, "linePrefix")(code) : after(code);
  }
  function after(code) {
    return code === null || markdownLineEnding(code) ? ok2(code) : nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/block-quote.js
var blockQuote = {
  continuation: {
    tokenize: tokenizeBlockQuoteContinuation
  },
  exit,
  name: "blockQuote",
  tokenize: tokenizeBlockQuoteStart
};
function tokenizeBlockQuoteStart(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    if (code === 62) {
      const state = self.containerState;
      if (!state.open) {
        effects.enter("blockQuote", {
          _container: true
        });
        state.open = true;
      }
      effects.enter("blockQuotePrefix");
      effects.enter("blockQuoteMarker");
      effects.consume(code);
      effects.exit("blockQuoteMarker");
      return after;
    }
    return nok(code);
  }
  function after(code) {
    if (markdownSpace(code)) {
      effects.enter("blockQuotePrefixWhitespace");
      effects.consume(code);
      effects.exit("blockQuotePrefixWhitespace");
      effects.exit("blockQuotePrefix");
      return ok2;
    }
    effects.exit("blockQuotePrefix");
    return ok2(code);
  }
}
function tokenizeBlockQuoteContinuation(effects, ok2, nok) {
  const self = this;
  return contStart;
  function contStart(code) {
    if (markdownSpace(code)) {
      return factorySpace(effects, contBefore, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4)(code);
    }
    return contBefore(code);
  }
  function contBefore(code) {
    return effects.attempt(blockQuote, ok2, nok)(code);
  }
}
function exit(effects) {
  effects.exit("blockQuote");
}

// node_modules/micromark-core-commonmark/lib/character-escape.js
var characterEscape = {
  name: "characterEscape",
  tokenize: tokenizeCharacterEscape
};
function tokenizeCharacterEscape(effects, ok2, nok) {
  return start;
  function start(code) {
    effects.enter("characterEscape");
    effects.enter("escapeMarker");
    effects.consume(code);
    effects.exit("escapeMarker");
    return inside;
  }
  function inside(code) {
    if (asciiPunctuation(code)) {
      effects.enter("characterEscapeValue");
      effects.consume(code);
      effects.exit("characterEscapeValue");
      effects.exit("characterEscape");
      return ok2;
    }
    return nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/character-reference.js
var characterReference = {
  name: "characterReference",
  tokenize: tokenizeCharacterReference
};
function tokenizeCharacterReference(effects, ok2, nok) {
  const self = this;
  let size = 0;
  let max;
  let test;
  return start;
  function start(code) {
    effects.enter("characterReference");
    effects.enter("characterReferenceMarker");
    effects.consume(code);
    effects.exit("characterReferenceMarker");
    return open5;
  }
  function open5(code) {
    if (code === 35) {
      effects.enter("characterReferenceMarkerNumeric");
      effects.consume(code);
      effects.exit("characterReferenceMarkerNumeric");
      return numeric;
    }
    effects.enter("characterReferenceValue");
    max = 31;
    test = asciiAlphanumeric;
    return value(code);
  }
  function numeric(code) {
    if (code === 88 || code === 120) {
      effects.enter("characterReferenceMarkerHexadecimal");
      effects.consume(code);
      effects.exit("characterReferenceMarkerHexadecimal");
      effects.enter("characterReferenceValue");
      max = 6;
      test = asciiHexDigit;
      return value;
    }
    effects.enter("characterReferenceValue");
    max = 7;
    test = asciiDigit;
    return value(code);
  }
  function value(code) {
    if (code === 59 && size) {
      const token = effects.exit("characterReferenceValue");
      if (test === asciiAlphanumeric && !decodeNamedCharacterReference(self.sliceSerialize(token))) {
        return nok(code);
      }
      effects.enter("characterReferenceMarker");
      effects.consume(code);
      effects.exit("characterReferenceMarker");
      effects.exit("characterReference");
      return ok2;
    }
    if (test(code) && size++ < max) {
      effects.consume(code);
      return value;
    }
    return nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/code-fenced.js
var nonLazyContinuation = {
  partial: true,
  tokenize: tokenizeNonLazyContinuation
};
var codeFenced = {
  concrete: true,
  name: "codeFenced",
  tokenize: tokenizeCodeFenced
};
function tokenizeCodeFenced(effects, ok2, nok) {
  const self = this;
  const closeStart = {
    partial: true,
    tokenize: tokenizeCloseStart
  };
  let initialPrefix = 0;
  let sizeOpen = 0;
  let marker;
  return start;
  function start(code) {
    return beforeSequenceOpen(code);
  }
  function beforeSequenceOpen(code) {
    const tail = self.events[self.events.length - 1];
    initialPrefix = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;
    marker = code;
    effects.enter("codeFenced");
    effects.enter("codeFencedFence");
    effects.enter("codeFencedFenceSequence");
    return sequenceOpen(code);
  }
  function sequenceOpen(code) {
    if (code === marker) {
      sizeOpen++;
      effects.consume(code);
      return sequenceOpen;
    }
    if (sizeOpen < 3) {
      return nok(code);
    }
    effects.exit("codeFencedFenceSequence");
    return markdownSpace(code) ? factorySpace(effects, infoBefore, "whitespace")(code) : infoBefore(code);
  }
  function infoBefore(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("codeFencedFence");
      return self.interrupt ? ok2(code) : effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);
    }
    effects.enter("codeFencedFenceInfo");
    effects.enter("chunkString", {
      contentType: "string"
    });
    return info(code);
  }
  function info(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("chunkString");
      effects.exit("codeFencedFenceInfo");
      return infoBefore(code);
    }
    if (markdownSpace(code)) {
      effects.exit("chunkString");
      effects.exit("codeFencedFenceInfo");
      return factorySpace(effects, metaBefore, "whitespace")(code);
    }
    if (code === 96 && code === marker) {
      return nok(code);
    }
    effects.consume(code);
    return info;
  }
  function metaBefore(code) {
    if (code === null || markdownLineEnding(code)) {
      return infoBefore(code);
    }
    effects.enter("codeFencedFenceMeta");
    effects.enter("chunkString", {
      contentType: "string"
    });
    return meta(code);
  }
  function meta(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("chunkString");
      effects.exit("codeFencedFenceMeta");
      return infoBefore(code);
    }
    if (code === 96 && code === marker) {
      return nok(code);
    }
    effects.consume(code);
    return meta;
  }
  function atNonLazyBreak(code) {
    return effects.attempt(closeStart, after, contentBefore)(code);
  }
  function contentBefore(code) {
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return contentStart;
  }
  function contentStart(code) {
    return initialPrefix > 0 && markdownSpace(code) ? factorySpace(effects, beforeContentChunk, "linePrefix", initialPrefix + 1)(code) : beforeContentChunk(code);
  }
  function beforeContentChunk(code) {
    if (code === null || markdownLineEnding(code)) {
      return effects.check(nonLazyContinuation, atNonLazyBreak, after)(code);
    }
    effects.enter("codeFlowValue");
    return contentChunk(code);
  }
  function contentChunk(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("codeFlowValue");
      return beforeContentChunk(code);
    }
    effects.consume(code);
    return contentChunk;
  }
  function after(code) {
    effects.exit("codeFenced");
    return ok2(code);
  }
  function tokenizeCloseStart(effects2, ok3, nok2) {
    let size = 0;
    return startBefore;
    function startBefore(code) {
      effects2.enter("lineEnding");
      effects2.consume(code);
      effects2.exit("lineEnding");
      return start2;
    }
    function start2(code) {
      effects2.enter("codeFencedFence");
      return markdownSpace(code) ? factorySpace(effects2, beforeSequenceClose, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4)(code) : beforeSequenceClose(code);
    }
    function beforeSequenceClose(code) {
      if (code === marker) {
        effects2.enter("codeFencedFenceSequence");
        return sequenceClose(code);
      }
      return nok2(code);
    }
    function sequenceClose(code) {
      if (code === marker) {
        size++;
        effects2.consume(code);
        return sequenceClose;
      }
      if (size >= sizeOpen) {
        effects2.exit("codeFencedFenceSequence");
        return markdownSpace(code) ? factorySpace(effects2, sequenceCloseAfter, "whitespace")(code) : sequenceCloseAfter(code);
      }
      return nok2(code);
    }
    function sequenceCloseAfter(code) {
      if (code === null || markdownLineEnding(code)) {
        effects2.exit("codeFencedFence");
        return ok3(code);
      }
      return nok2(code);
    }
  }
}
function tokenizeNonLazyContinuation(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    if (code === null) {
      return nok(code);
    }
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return lineStart;
  }
  function lineStart(code) {
    return self.parser.lazy[self.now().line] ? nok(code) : ok2(code);
  }
}

// node_modules/micromark-core-commonmark/lib/code-indented.js
var codeIndented = {
  name: "codeIndented",
  tokenize: tokenizeCodeIndented
};
var furtherStart = {
  partial: true,
  tokenize: tokenizeFurtherStart
};
function tokenizeCodeIndented(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    effects.enter("codeIndented");
    return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
  }
  function afterPrefix(code) {
    const tail = self.events[self.events.length - 1];
    return tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? atBreak(code) : nok(code);
  }
  function atBreak(code) {
    if (code === null) {
      return after(code);
    }
    if (markdownLineEnding(code)) {
      return effects.attempt(furtherStart, atBreak, after)(code);
    }
    effects.enter("codeFlowValue");
    return inside(code);
  }
  function inside(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("codeFlowValue");
      return atBreak(code);
    }
    effects.consume(code);
    return inside;
  }
  function after(code) {
    effects.exit("codeIndented");
    return ok2(code);
  }
}
function tokenizeFurtherStart(effects, ok2, nok) {
  const self = this;
  return furtherStart2;
  function furtherStart2(code) {
    if (self.parser.lazy[self.now().line]) {
      return nok(code);
    }
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      return furtherStart2;
    }
    return factorySpace(effects, afterPrefix, "linePrefix", 4 + 1)(code);
  }
  function afterPrefix(code) {
    const tail = self.events[self.events.length - 1];
    return tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4 ? ok2(code) : markdownLineEnding(code) ? furtherStart2(code) : nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/code-text.js
var codeText = {
  name: "codeText",
  previous,
  resolve: resolveCodeText,
  tokenize: tokenizeCodeText
};
function resolveCodeText(events) {
  let tailExitIndex = events.length - 4;
  let headEnterIndex = 3;
  let index2;
  let enter;
  if ((events[headEnterIndex][1].type === "lineEnding" || events[headEnterIndex][1].type === "space") && (events[tailExitIndex][1].type === "lineEnding" || events[tailExitIndex][1].type === "space")) {
    index2 = headEnterIndex;
    while (++index2 < tailExitIndex) {
      if (events[index2][1].type === "codeTextData") {
        events[headEnterIndex][1].type = "codeTextPadding";
        events[tailExitIndex][1].type = "codeTextPadding";
        headEnterIndex += 2;
        tailExitIndex -= 2;
        break;
      }
    }
  }
  index2 = headEnterIndex - 1;
  tailExitIndex++;
  while (++index2 <= tailExitIndex) {
    if (enter === void 0) {
      if (index2 !== tailExitIndex && events[index2][1].type !== "lineEnding") {
        enter = index2;
      }
    } else if (index2 === tailExitIndex || events[index2][1].type === "lineEnding") {
      events[enter][1].type = "codeTextData";
      if (index2 !== enter + 2) {
        events[enter][1].end = events[index2 - 1][1].end;
        events.splice(enter + 2, index2 - enter - 2);
        tailExitIndex -= index2 - enter - 2;
        index2 = enter + 2;
      }
      enter = void 0;
    }
  }
  return events;
}
function previous(code) {
  return code !== 96 || this.events[this.events.length - 1][1].type === "characterEscape";
}
function tokenizeCodeText(effects, ok2, nok) {
  const self = this;
  let sizeOpen = 0;
  let size;
  let token;
  return start;
  function start(code) {
    effects.enter("codeText");
    effects.enter("codeTextSequence");
    return sequenceOpen(code);
  }
  function sequenceOpen(code) {
    if (code === 96) {
      effects.consume(code);
      sizeOpen++;
      return sequenceOpen;
    }
    effects.exit("codeTextSequence");
    return between(code);
  }
  function between(code) {
    if (code === null) {
      return nok(code);
    }
    if (code === 32) {
      effects.enter("space");
      effects.consume(code);
      effects.exit("space");
      return between;
    }
    if (code === 96) {
      token = effects.enter("codeTextSequence");
      size = 0;
      return sequenceClose(code);
    }
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      return between;
    }
    effects.enter("codeTextData");
    return data(code);
  }
  function data(code) {
    if (code === null || code === 32 || code === 96 || markdownLineEnding(code)) {
      effects.exit("codeTextData");
      return between(code);
    }
    effects.consume(code);
    return data;
  }
  function sequenceClose(code) {
    if (code === 96) {
      effects.consume(code);
      size++;
      return sequenceClose;
    }
    if (size === sizeOpen) {
      effects.exit("codeTextSequence");
      effects.exit("codeText");
      return ok2(code);
    }
    token.type = "codeTextData";
    return data(code);
  }
}

// node_modules/micromark-util-subtokenize/lib/splice-buffer.js
var SpliceBuffer = class {
  /**
   * @param {ReadonlyArray<T> | null | undefined} [initial]
   *   Initial items (optional).
   * @returns
   *   Splice buffer.
   */
  constructor(initial) {
    this.left = initial ? [...initial] : [];
    this.right = [];
  }
  /**
   * Array access;
   * does not move the cursor.
   *
   * @param {number} index
   *   Index.
   * @return {T}
   *   Item.
   */
  get(index2) {
    if (index2 < 0 || index2 >= this.left.length + this.right.length) {
      throw new RangeError("Cannot access index `" + index2 + "` in a splice buffer of size `" + (this.left.length + this.right.length) + "`");
    }
    if (index2 < this.left.length) return this.left[index2];
    return this.right[this.right.length - index2 + this.left.length - 1];
  }
  /**
   * The length of the splice buffer, one greater than the largest index in the
   * array.
   */
  get length() {
    return this.left.length + this.right.length;
  }
  /**
   * Remove and return `list[0]`;
   * moves the cursor to `0`.
   *
   * @returns {T | undefined}
   *   Item, optional.
   */
  shift() {
    this.setCursor(0);
    return this.right.pop();
  }
  /**
   * Slice the buffer to get an array;
   * does not move the cursor.
   *
   * @param {number} start
   *   Start.
   * @param {number | null | undefined} [end]
   *   End (optional).
   * @returns {Array<T>}
   *   Array of items.
   */
  slice(start, end) {
    const stop = end === null || end === void 0 ? Number.POSITIVE_INFINITY : end;
    if (stop < this.left.length) {
      return this.left.slice(start, stop);
    }
    if (start > this.left.length) {
      return this.right.slice(this.right.length - stop + this.left.length, this.right.length - start + this.left.length).reverse();
    }
    return this.left.slice(start).concat(this.right.slice(this.right.length - stop + this.left.length).reverse());
  }
  /**
   * Mimics the behavior of Array.prototype.splice() except for the change of
   * interface necessary to avoid segfaults when patching in very large arrays.
   *
   * This operation moves cursor is moved to `start` and results in the cursor
   * placed after any inserted items.
   *
   * @param {number} start
   *   Start;
   *   zero-based index at which to start changing the array;
   *   negative numbers count backwards from the end of the array and values
   *   that are out-of bounds are clamped to the appropriate end of the array.
   * @param {number | null | undefined} [deleteCount=0]
   *   Delete count (default: `0`);
   *   maximum number of elements to delete, starting from start.
   * @param {Array<T> | null | undefined} [items=[]]
   *   Items to include in place of the deleted items (default: `[]`).
   * @return {Array<T>}
   *   Any removed items.
   */
  splice(start, deleteCount, items) {
    const count = deleteCount || 0;
    this.setCursor(Math.trunc(start));
    const removed = this.right.splice(this.right.length - count, Number.POSITIVE_INFINITY);
    if (items) chunkedPush(this.left, items);
    return removed.reverse();
  }
  /**
   * Remove and return the highest-numbered item in the array, so
   * `list[list.length - 1]`;
   * Moves the cursor to `length`.
   *
   * @returns {T | undefined}
   *   Item, optional.
   */
  pop() {
    this.setCursor(Number.POSITIVE_INFINITY);
    return this.left.pop();
  }
  /**
   * Inserts a single item to the high-numbered side of the array;
   * moves the cursor to `length`.
   *
   * @param {T} item
   *   Item.
   * @returns {undefined}
   *   Nothing.
   */
  push(item) {
    this.setCursor(Number.POSITIVE_INFINITY);
    this.left.push(item);
  }
  /**
   * Inserts many items to the high-numbered side of the array.
   * Moves the cursor to `length`.
   *
   * @param {Array<T>} items
   *   Items.
   * @returns {undefined}
   *   Nothing.
   */
  pushMany(items) {
    this.setCursor(Number.POSITIVE_INFINITY);
    chunkedPush(this.left, items);
  }
  /**
   * Inserts a single item to the low-numbered side of the array;
   * Moves the cursor to `0`.
   *
   * @param {T} item
   *   Item.
   * @returns {undefined}
   *   Nothing.
   */
  unshift(item) {
    this.setCursor(0);
    this.right.push(item);
  }
  /**
   * Inserts many items to the low-numbered side of the array;
   * moves the cursor to `0`.
   *
   * @param {Array<T>} items
   *   Items.
   * @returns {undefined}
   *   Nothing.
   */
  unshiftMany(items) {
    this.setCursor(0);
    chunkedPush(this.right, items.reverse());
  }
  /**
   * Move the cursor to a specific position in the array. Requires
   * time proportional to the distance moved.
   *
   * If `n < 0`, the cursor will end up at the beginning.
   * If `n > length`, the cursor will end up at the end.
   *
   * @param {number} n
   *   Position.
   * @return {undefined}
   *   Nothing.
   */
  setCursor(n) {
    if (n === this.left.length || n > this.left.length && this.right.length === 0 || n < 0 && this.left.length === 0) return;
    if (n < this.left.length) {
      const removed = this.left.splice(n, Number.POSITIVE_INFINITY);
      chunkedPush(this.right, removed.reverse());
    } else {
      const removed = this.right.splice(this.left.length + this.right.length - n, Number.POSITIVE_INFINITY);
      chunkedPush(this.left, removed.reverse());
    }
  }
};
function chunkedPush(list2, right) {
  let chunkStart = 0;
  if (right.length < 1e4) {
    list2.push(...right);
  } else {
    while (chunkStart < right.length) {
      list2.push(...right.slice(chunkStart, chunkStart + 1e4));
      chunkStart += 1e4;
    }
  }
}

// node_modules/micromark-util-subtokenize/index.js
function subtokenize(eventsArray) {
  const jumps = {};
  let index2 = -1;
  let event;
  let lineIndex;
  let otherIndex;
  let otherEvent;
  let parameters;
  let subevents;
  let more;
  const events = new SpliceBuffer(eventsArray);
  while (++index2 < events.length) {
    while (index2 in jumps) {
      index2 = jumps[index2];
    }
    event = events.get(index2);
    if (index2 && event[1].type === "chunkFlow" && events.get(index2 - 1)[1].type === "listItemPrefix") {
      subevents = event[1]._tokenizer.events;
      otherIndex = 0;
      if (otherIndex < subevents.length && subevents[otherIndex][1].type === "lineEndingBlank") {
        otherIndex += 2;
      }
      if (otherIndex < subevents.length && subevents[otherIndex][1].type === "content") {
        while (++otherIndex < subevents.length) {
          if (subevents[otherIndex][1].type === "content") {
            break;
          }
          if (subevents[otherIndex][1].type === "chunkText") {
            subevents[otherIndex][1]._isInFirstContentOfListItem = true;
            otherIndex++;
          }
        }
      }
    }
    if (event[0] === "enter") {
      if (event[1].contentType) {
        Object.assign(jumps, subcontent(events, index2));
        index2 = jumps[index2];
        more = true;
      }
    } else if (event[1]._container) {
      otherIndex = index2;
      lineIndex = void 0;
      while (otherIndex--) {
        otherEvent = events.get(otherIndex);
        if (otherEvent[1].type === "lineEnding" || otherEvent[1].type === "lineEndingBlank") {
          if (otherEvent[0] === "enter") {
            if (lineIndex) {
              events.get(lineIndex)[1].type = "lineEndingBlank";
            }
            otherEvent[1].type = "lineEnding";
            lineIndex = otherIndex;
          }
        } else if (otherEvent[1].type === "linePrefix" || otherEvent[1].type === "listItemIndent") {
        } else {
          break;
        }
      }
      if (lineIndex) {
        event[1].end = {
          ...events.get(lineIndex)[1].start
        };
        parameters = events.slice(lineIndex, index2);
        parameters.unshift(event);
        events.splice(lineIndex, index2 - lineIndex + 1, parameters);
      }
    }
  }
  splice(eventsArray, 0, Number.POSITIVE_INFINITY, events.slice(0));
  return !more;
}
function subcontent(events, eventIndex) {
  const token = events.get(eventIndex)[1];
  const context = events.get(eventIndex)[2];
  let startPosition = eventIndex - 1;
  const startPositions = [];
  let tokenizer = token._tokenizer;
  if (!tokenizer) {
    tokenizer = context.parser[token.contentType](token.start);
    if (token._contentTypeTextTrailing) {
      tokenizer._contentTypeTextTrailing = true;
    }
  }
  const childEvents = tokenizer.events;
  const jumps = [];
  const gaps = {};
  let stream;
  let previous2;
  let index2 = -1;
  let current = token;
  let adjust = 0;
  let start = 0;
  const breaks = [start];
  while (current) {
    while (events.get(++startPosition)[1] !== current) {
    }
    startPositions.push(startPosition);
    if (!current._tokenizer) {
      stream = context.sliceStream(current);
      if (!current.next) {
        stream.push(null);
      }
      if (previous2) {
        tokenizer.defineSkip(current.start);
      }
      if (current._isInFirstContentOfListItem) {
        tokenizer._gfmTasklistFirstContentOfListItem = true;
      }
      tokenizer.write(stream);
      if (current._isInFirstContentOfListItem) {
        tokenizer._gfmTasklistFirstContentOfListItem = void 0;
      }
    }
    previous2 = current;
    current = current.next;
  }
  current = token;
  while (++index2 < childEvents.length) {
    if (
      // Find a void token that includes a break.
      childEvents[index2][0] === "exit" && childEvents[index2 - 1][0] === "enter" && childEvents[index2][1].type === childEvents[index2 - 1][1].type && childEvents[index2][1].start.line !== childEvents[index2][1].end.line
    ) {
      start = index2 + 1;
      breaks.push(start);
      current._tokenizer = void 0;
      current.previous = void 0;
      current = current.next;
    }
  }
  tokenizer.events = [];
  if (current) {
    current._tokenizer = void 0;
    current.previous = void 0;
  } else {
    breaks.pop();
  }
  index2 = breaks.length;
  while (index2--) {
    const slice = childEvents.slice(breaks[index2], breaks[index2 + 1]);
    const start2 = startPositions.pop();
    jumps.push([start2, start2 + slice.length - 1]);
    events.splice(start2, 2, slice);
  }
  jumps.reverse();
  index2 = -1;
  while (++index2 < jumps.length) {
    gaps[adjust + jumps[index2][0]] = adjust + jumps[index2][1];
    adjust += jumps[index2][1] - jumps[index2][0] - 1;
  }
  return gaps;
}

// node_modules/micromark-core-commonmark/lib/content.js
var content2 = {
  resolve: resolveContent,
  tokenize: tokenizeContent
};
var continuationConstruct = {
  partial: true,
  tokenize: tokenizeContinuation
};
function resolveContent(events) {
  subtokenize(events);
  return events;
}
function tokenizeContent(effects, ok2) {
  let previous2;
  return chunkStart;
  function chunkStart(code) {
    effects.enter("content");
    previous2 = effects.enter("chunkContent", {
      contentType: "content"
    });
    return chunkInside(code);
  }
  function chunkInside(code) {
    if (code === null) {
      return contentEnd(code);
    }
    if (markdownLineEnding(code)) {
      return effects.check(continuationConstruct, contentContinue, contentEnd)(code);
    }
    effects.consume(code);
    return chunkInside;
  }
  function contentEnd(code) {
    effects.exit("chunkContent");
    effects.exit("content");
    return ok2(code);
  }
  function contentContinue(code) {
    effects.consume(code);
    effects.exit("chunkContent");
    previous2.next = effects.enter("chunkContent", {
      contentType: "content",
      previous: previous2
    });
    previous2 = previous2.next;
    return chunkInside;
  }
}
function tokenizeContinuation(effects, ok2, nok) {
  const self = this;
  return startLookahead;
  function startLookahead(code) {
    effects.exit("chunkContent");
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return factorySpace(effects, prefixed, "linePrefix");
  }
  function prefixed(code) {
    if (code === null || markdownLineEnding(code)) {
      return nok(code);
    }
    const tail = self.events[self.events.length - 1];
    if (!self.parser.constructs.disable.null.includes("codeIndented") && tail && tail[1].type === "linePrefix" && tail[2].sliceSerialize(tail[1], true).length >= 4) {
      return ok2(code);
    }
    return effects.interrupt(self.parser.constructs.flow, nok, ok2)(code);
  }
}

// node_modules/micromark-factory-destination/index.js
function factoryDestination(effects, ok2, nok, type, literalType, literalMarkerType, rawType, stringType, max) {
  const limit = max || Number.POSITIVE_INFINITY;
  let balance = 0;
  return start;
  function start(code) {
    if (code === 60) {
      effects.enter(type);
      effects.enter(literalType);
      effects.enter(literalMarkerType);
      effects.consume(code);
      effects.exit(literalMarkerType);
      return enclosedBefore;
    }
    if (code === null || code === 32 || code === 41 || asciiControl(code)) {
      return nok(code);
    }
    effects.enter(type);
    effects.enter(rawType);
    effects.enter(stringType);
    effects.enter("chunkString", {
      contentType: "string"
    });
    return raw(code);
  }
  function enclosedBefore(code) {
    if (code === 62) {
      effects.enter(literalMarkerType);
      effects.consume(code);
      effects.exit(literalMarkerType);
      effects.exit(literalType);
      effects.exit(type);
      return ok2;
    }
    effects.enter(stringType);
    effects.enter("chunkString", {
      contentType: "string"
    });
    return enclosed(code);
  }
  function enclosed(code) {
    if (code === 62) {
      effects.exit("chunkString");
      effects.exit(stringType);
      return enclosedBefore(code);
    }
    if (code === null || code === 60 || markdownLineEnding(code)) {
      return nok(code);
    }
    effects.consume(code);
    return code === 92 ? enclosedEscape : enclosed;
  }
  function enclosedEscape(code) {
    if (code === 60 || code === 62 || code === 92) {
      effects.consume(code);
      return enclosed;
    }
    return enclosed(code);
  }
  function raw(code) {
    if (!balance && (code === null || code === 41 || markdownLineEndingOrSpace(code))) {
      effects.exit("chunkString");
      effects.exit(stringType);
      effects.exit(rawType);
      effects.exit(type);
      return ok2(code);
    }
    if (balance < limit && code === 40) {
      effects.consume(code);
      balance++;
      return raw;
    }
    if (code === 41) {
      effects.consume(code);
      balance--;
      return raw;
    }
    if (code === null || code === 32 || code === 40 || asciiControl(code)) {
      return nok(code);
    }
    effects.consume(code);
    return code === 92 ? rawEscape : raw;
  }
  function rawEscape(code) {
    if (code === 40 || code === 41 || code === 92) {
      effects.consume(code);
      return raw;
    }
    return raw(code);
  }
}

// node_modules/micromark-factory-label/index.js
function factoryLabel(effects, ok2, nok, type, markerType, stringType) {
  const self = this;
  let size = 0;
  let seen;
  return start;
  function start(code) {
    effects.enter(type);
    effects.enter(markerType);
    effects.consume(code);
    effects.exit(markerType);
    effects.enter(stringType);
    return atBreak;
  }
  function atBreak(code) {
    if (size > 999 || code === null || code === 91 || code === 93 && !seen || // To do: remove in the future once we’ve switched from
    // `micromark-extension-footnote` to `micromark-extension-gfm-footnote`,
    // which doesn’t need this.
    // Hidden footnotes hook.
    /* c8 ignore next 3 */
    code === 94 && !size && "_hiddenFootnoteSupport" in self.parser.constructs) {
      return nok(code);
    }
    if (code === 93) {
      effects.exit(stringType);
      effects.enter(markerType);
      effects.consume(code);
      effects.exit(markerType);
      effects.exit(type);
      return ok2;
    }
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      return atBreak;
    }
    effects.enter("chunkString", {
      contentType: "string"
    });
    return labelInside(code);
  }
  function labelInside(code) {
    if (code === null || code === 91 || code === 93 || markdownLineEnding(code) || size++ > 999) {
      effects.exit("chunkString");
      return atBreak(code);
    }
    effects.consume(code);
    if (!seen) seen = !markdownSpace(code);
    return code === 92 ? labelEscape : labelInside;
  }
  function labelEscape(code) {
    if (code === 91 || code === 92 || code === 93) {
      effects.consume(code);
      size++;
      return labelInside;
    }
    return labelInside(code);
  }
}

// node_modules/micromark-factory-title/index.js
function factoryTitle(effects, ok2, nok, type, markerType, stringType) {
  let marker;
  return start;
  function start(code) {
    if (code === 34 || code === 39 || code === 40) {
      effects.enter(type);
      effects.enter(markerType);
      effects.consume(code);
      effects.exit(markerType);
      marker = code === 40 ? 41 : code;
      return begin;
    }
    return nok(code);
  }
  function begin(code) {
    if (code === marker) {
      effects.enter(markerType);
      effects.consume(code);
      effects.exit(markerType);
      effects.exit(type);
      return ok2;
    }
    effects.enter(stringType);
    return atBreak(code);
  }
  function atBreak(code) {
    if (code === marker) {
      effects.exit(stringType);
      return begin(marker);
    }
    if (code === null) {
      return nok(code);
    }
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      return factorySpace(effects, atBreak, "linePrefix");
    }
    effects.enter("chunkString", {
      contentType: "string"
    });
    return inside(code);
  }
  function inside(code) {
    if (code === marker || code === null || markdownLineEnding(code)) {
      effects.exit("chunkString");
      return atBreak(code);
    }
    effects.consume(code);
    return code === 92 ? escape : inside;
  }
  function escape(code) {
    if (code === marker || code === 92) {
      effects.consume(code);
      return inside;
    }
    return inside(code);
  }
}

// node_modules/micromark-factory-whitespace/index.js
function factoryWhitespace(effects, ok2) {
  let seen;
  return start;
  function start(code) {
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      seen = true;
      return start;
    }
    if (markdownSpace(code)) {
      return factorySpace(effects, start, seen ? "linePrefix" : "lineSuffix")(code);
    }
    return ok2(code);
  }
}

// node_modules/micromark-core-commonmark/lib/definition.js
var definition = {
  name: "definition",
  tokenize: tokenizeDefinition
};
var titleBefore = {
  partial: true,
  tokenize: tokenizeTitleBefore
};
function tokenizeDefinition(effects, ok2, nok) {
  const self = this;
  let identifier;
  return start;
  function start(code) {
    effects.enter("definition");
    return before(code);
  }
  function before(code) {
    return factoryLabel.call(
      self,
      effects,
      labelAfter,
      // Note: we don’t need to reset the way `markdown-rs` does.
      nok,
      "definitionLabel",
      "definitionLabelMarker",
      "definitionLabelString"
    )(code);
  }
  function labelAfter(code) {
    identifier = normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1));
    if (code === 58) {
      effects.enter("definitionMarker");
      effects.consume(code);
      effects.exit("definitionMarker");
      return markerAfter;
    }
    return nok(code);
  }
  function markerAfter(code) {
    return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, destinationBefore)(code) : destinationBefore(code);
  }
  function destinationBefore(code) {
    return factoryDestination(
      effects,
      destinationAfter,
      // Note: we don’t need to reset the way `markdown-rs` does.
      nok,
      "definitionDestination",
      "definitionDestinationLiteral",
      "definitionDestinationLiteralMarker",
      "definitionDestinationRaw",
      "definitionDestinationString"
    )(code);
  }
  function destinationAfter(code) {
    return effects.attempt(titleBefore, after, after)(code);
  }
  function after(code) {
    return markdownSpace(code) ? factorySpace(effects, afterWhitespace, "whitespace")(code) : afterWhitespace(code);
  }
  function afterWhitespace(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("definition");
      self.parser.defined.push(identifier);
      return ok2(code);
    }
    return nok(code);
  }
}
function tokenizeTitleBefore(effects, ok2, nok) {
  return titleBefore2;
  function titleBefore2(code) {
    return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, beforeMarker)(code) : nok(code);
  }
  function beforeMarker(code) {
    return factoryTitle(effects, titleAfter, nok, "definitionTitle", "definitionTitleMarker", "definitionTitleString")(code);
  }
  function titleAfter(code) {
    return markdownSpace(code) ? factorySpace(effects, titleAfterOptionalWhitespace, "whitespace")(code) : titleAfterOptionalWhitespace(code);
  }
  function titleAfterOptionalWhitespace(code) {
    return code === null || markdownLineEnding(code) ? ok2(code) : nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/hard-break-escape.js
var hardBreakEscape = {
  name: "hardBreakEscape",
  tokenize: tokenizeHardBreakEscape
};
function tokenizeHardBreakEscape(effects, ok2, nok) {
  return start;
  function start(code) {
    effects.enter("hardBreakEscape");
    effects.consume(code);
    return after;
  }
  function after(code) {
    if (markdownLineEnding(code)) {
      effects.exit("hardBreakEscape");
      return ok2(code);
    }
    return nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/heading-atx.js
var headingAtx = {
  name: "headingAtx",
  resolve: resolveHeadingAtx,
  tokenize: tokenizeHeadingAtx
};
function resolveHeadingAtx(events, context) {
  let contentEnd = events.length - 2;
  let contentStart = 3;
  let content3;
  let text3;
  if (events[contentStart][1].type === "whitespace") {
    contentStart += 2;
  }
  if (contentEnd - 2 > contentStart && events[contentEnd][1].type === "whitespace") {
    contentEnd -= 2;
  }
  if (events[contentEnd][1].type === "atxHeadingSequence" && (contentStart === contentEnd - 1 || contentEnd - 4 > contentStart && events[contentEnd - 2][1].type === "whitespace")) {
    contentEnd -= contentStart + 1 === contentEnd ? 2 : 4;
  }
  if (contentEnd > contentStart) {
    content3 = {
      type: "atxHeadingText",
      start: events[contentStart][1].start,
      end: events[contentEnd][1].end
    };
    text3 = {
      type: "chunkText",
      start: events[contentStart][1].start,
      end: events[contentEnd][1].end,
      contentType: "text"
    };
    splice(events, contentStart, contentEnd - contentStart + 1, [["enter", content3, context], ["enter", text3, context], ["exit", text3, context], ["exit", content3, context]]);
  }
  return events;
}
function tokenizeHeadingAtx(effects, ok2, nok) {
  let size = 0;
  return start;
  function start(code) {
    effects.enter("atxHeading");
    return before(code);
  }
  function before(code) {
    effects.enter("atxHeadingSequence");
    return sequenceOpen(code);
  }
  function sequenceOpen(code) {
    if (code === 35 && size++ < 6) {
      effects.consume(code);
      return sequenceOpen;
    }
    if (code === null || markdownLineEndingOrSpace(code)) {
      effects.exit("atxHeadingSequence");
      return atBreak(code);
    }
    return nok(code);
  }
  function atBreak(code) {
    if (code === 35) {
      effects.enter("atxHeadingSequence");
      return sequenceFurther(code);
    }
    if (code === null || markdownLineEnding(code)) {
      effects.exit("atxHeading");
      return ok2(code);
    }
    if (markdownSpace(code)) {
      return factorySpace(effects, atBreak, "whitespace")(code);
    }
    effects.enter("atxHeadingText");
    return data(code);
  }
  function sequenceFurther(code) {
    if (code === 35) {
      effects.consume(code);
      return sequenceFurther;
    }
    effects.exit("atxHeadingSequence");
    return atBreak(code);
  }
  function data(code) {
    if (code === null || code === 35 || markdownLineEndingOrSpace(code)) {
      effects.exit("atxHeadingText");
      return atBreak(code);
    }
    effects.consume(code);
    return data;
  }
}

// node_modules/micromark-util-html-tag-name/index.js
var htmlBlockNames = [
  "address",
  "article",
  "aside",
  "base",
  "basefont",
  "blockquote",
  "body",
  "caption",
  "center",
  "col",
  "colgroup",
  "dd",
  "details",
  "dialog",
  "dir",
  "div",
  "dl",
  "dt",
  "fieldset",
  "figcaption",
  "figure",
  "footer",
  "form",
  "frame",
  "frameset",
  "h1",
  "h2",
  "h3",
  "h4",
  "h5",
  "h6",
  "head",
  "header",
  "hr",
  "html",
  "iframe",
  "legend",
  "li",
  "link",
  "main",
  "menu",
  "menuitem",
  "nav",
  "noframes",
  "ol",
  "optgroup",
  "option",
  "p",
  "param",
  "search",
  "section",
  "summary",
  "table",
  "tbody",
  "td",
  "tfoot",
  "th",
  "thead",
  "title",
  "tr",
  "track",
  "ul"
];
var htmlRawNames = ["pre", "script", "style", "textarea"];

// node_modules/micromark-core-commonmark/lib/html-flow.js
var htmlFlow = {
  concrete: true,
  name: "htmlFlow",
  resolveTo: resolveToHtmlFlow,
  tokenize: tokenizeHtmlFlow
};
var blankLineBefore = {
  partial: true,
  tokenize: tokenizeBlankLineBefore
};
var nonLazyContinuationStart = {
  partial: true,
  tokenize: tokenizeNonLazyContinuationStart
};
function resolveToHtmlFlow(events) {
  let index2 = events.length;
  while (index2--) {
    if (events[index2][0] === "enter" && events[index2][1].type === "htmlFlow") {
      break;
    }
  }
  if (index2 > 1 && events[index2 - 2][1].type === "linePrefix") {
    events[index2][1].start = events[index2 - 2][1].start;
    events[index2 + 1][1].start = events[index2 - 2][1].start;
    events.splice(index2 - 2, 2);
  }
  return events;
}
function tokenizeHtmlFlow(effects, ok2, nok) {
  const self = this;
  let marker;
  let closingTag;
  let buffer;
  let index2;
  let markerB;
  return start;
  function start(code) {
    return before(code);
  }
  function before(code) {
    effects.enter("htmlFlow");
    effects.enter("htmlFlowData");
    effects.consume(code);
    return open5;
  }
  function open5(code) {
    if (code === 33) {
      effects.consume(code);
      return declarationOpen;
    }
    if (code === 47) {
      effects.consume(code);
      closingTag = true;
      return tagCloseStart;
    }
    if (code === 63) {
      effects.consume(code);
      marker = 3;
      return self.interrupt ? ok2 : continuationDeclarationInside;
    }
    if (asciiAlpha(code)) {
      effects.consume(code);
      buffer = String.fromCharCode(code);
      return tagName;
    }
    return nok(code);
  }
  function declarationOpen(code) {
    if (code === 45) {
      effects.consume(code);
      marker = 2;
      return commentOpenInside;
    }
    if (code === 91) {
      effects.consume(code);
      marker = 5;
      index2 = 0;
      return cdataOpenInside;
    }
    if (asciiAlpha(code)) {
      effects.consume(code);
      marker = 4;
      return self.interrupt ? ok2 : continuationDeclarationInside;
    }
    return nok(code);
  }
  function commentOpenInside(code) {
    if (code === 45) {
      effects.consume(code);
      return self.interrupt ? ok2 : continuationDeclarationInside;
    }
    return nok(code);
  }
  function cdataOpenInside(code) {
    const value = "CDATA[";
    if (code === value.charCodeAt(index2++)) {
      effects.consume(code);
      if (index2 === value.length) {
        return self.interrupt ? ok2 : continuation;
      }
      return cdataOpenInside;
    }
    return nok(code);
  }
  function tagCloseStart(code) {
    if (asciiAlpha(code)) {
      effects.consume(code);
      buffer = String.fromCharCode(code);
      return tagName;
    }
    return nok(code);
  }
  function tagName(code) {
    if (code === null || code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
      const slash = code === 47;
      const name = buffer.toLowerCase();
      if (!slash && !closingTag && htmlRawNames.includes(name)) {
        marker = 1;
        return self.interrupt ? ok2(code) : continuation(code);
      }
      if (htmlBlockNames.includes(buffer.toLowerCase())) {
        marker = 6;
        if (slash) {
          effects.consume(code);
          return basicSelfClosing;
        }
        return self.interrupt ? ok2(code) : continuation(code);
      }
      marker = 7;
      return self.interrupt && !self.parser.lazy[self.now().line] ? nok(code) : closingTag ? completeClosingTagAfter(code) : completeAttributeNameBefore(code);
    }
    if (code === 45 || asciiAlphanumeric(code)) {
      effects.consume(code);
      buffer += String.fromCharCode(code);
      return tagName;
    }
    return nok(code);
  }
  function basicSelfClosing(code) {
    if (code === 62) {
      effects.consume(code);
      return self.interrupt ? ok2 : continuation;
    }
    return nok(code);
  }
  function completeClosingTagAfter(code) {
    if (markdownSpace(code)) {
      effects.consume(code);
      return completeClosingTagAfter;
    }
    return completeEnd(code);
  }
  function completeAttributeNameBefore(code) {
    if (code === 47) {
      effects.consume(code);
      return completeEnd;
    }
    if (code === 58 || code === 95 || asciiAlpha(code)) {
      effects.consume(code);
      return completeAttributeName;
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return completeAttributeNameBefore;
    }
    return completeEnd(code);
  }
  function completeAttributeName(code) {
    if (code === 45 || code === 46 || code === 58 || code === 95 || asciiAlphanumeric(code)) {
      effects.consume(code);
      return completeAttributeName;
    }
    return completeAttributeNameAfter(code);
  }
  function completeAttributeNameAfter(code) {
    if (code === 61) {
      effects.consume(code);
      return completeAttributeValueBefore;
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return completeAttributeNameAfter;
    }
    return completeAttributeNameBefore(code);
  }
  function completeAttributeValueBefore(code) {
    if (code === null || code === 60 || code === 61 || code === 62 || code === 96) {
      return nok(code);
    }
    if (code === 34 || code === 39) {
      effects.consume(code);
      markerB = code;
      return completeAttributeValueQuoted;
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return completeAttributeValueBefore;
    }
    return completeAttributeValueUnquoted(code);
  }
  function completeAttributeValueQuoted(code) {
    if (code === markerB) {
      effects.consume(code);
      markerB = null;
      return completeAttributeValueQuotedAfter;
    }
    if (code === null || markdownLineEnding(code)) {
      return nok(code);
    }
    effects.consume(code);
    return completeAttributeValueQuoted;
  }
  function completeAttributeValueUnquoted(code) {
    if (code === null || code === 34 || code === 39 || code === 47 || code === 60 || code === 61 || code === 62 || code === 96 || markdownLineEndingOrSpace(code)) {
      return completeAttributeNameAfter(code);
    }
    effects.consume(code);
    return completeAttributeValueUnquoted;
  }
  function completeAttributeValueQuotedAfter(code) {
    if (code === 47 || code === 62 || markdownSpace(code)) {
      return completeAttributeNameBefore(code);
    }
    return nok(code);
  }
  function completeEnd(code) {
    if (code === 62) {
      effects.consume(code);
      return completeAfter;
    }
    return nok(code);
  }
  function completeAfter(code) {
    if (code === null || markdownLineEnding(code)) {
      return continuation(code);
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return completeAfter;
    }
    return nok(code);
  }
  function continuation(code) {
    if (code === 45 && marker === 2) {
      effects.consume(code);
      return continuationCommentInside;
    }
    if (code === 60 && marker === 1) {
      effects.consume(code);
      return continuationRawTagOpen;
    }
    if (code === 62 && marker === 4) {
      effects.consume(code);
      return continuationClose;
    }
    if (code === 63 && marker === 3) {
      effects.consume(code);
      return continuationDeclarationInside;
    }
    if (code === 93 && marker === 5) {
      effects.consume(code);
      return continuationCdataInside;
    }
    if (markdownLineEnding(code) && (marker === 6 || marker === 7)) {
      effects.exit("htmlFlowData");
      return effects.check(blankLineBefore, continuationAfter, continuationStart)(code);
    }
    if (code === null || markdownLineEnding(code)) {
      effects.exit("htmlFlowData");
      return continuationStart(code);
    }
    effects.consume(code);
    return continuation;
  }
  function continuationStart(code) {
    return effects.check(nonLazyContinuationStart, continuationStartNonLazy, continuationAfter)(code);
  }
  function continuationStartNonLazy(code) {
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return continuationBefore;
  }
  function continuationBefore(code) {
    if (code === null || markdownLineEnding(code)) {
      return continuationStart(code);
    }
    effects.enter("htmlFlowData");
    return continuation(code);
  }
  function continuationCommentInside(code) {
    if (code === 45) {
      effects.consume(code);
      return continuationDeclarationInside;
    }
    return continuation(code);
  }
  function continuationRawTagOpen(code) {
    if (code === 47) {
      effects.consume(code);
      buffer = "";
      return continuationRawEndTag;
    }
    return continuation(code);
  }
  function continuationRawEndTag(code) {
    if (code === 62) {
      const name = buffer.toLowerCase();
      if (htmlRawNames.includes(name)) {
        effects.consume(code);
        return continuationClose;
      }
      return continuation(code);
    }
    if (asciiAlpha(code) && buffer.length < 8) {
      effects.consume(code);
      buffer += String.fromCharCode(code);
      return continuationRawEndTag;
    }
    return continuation(code);
  }
  function continuationCdataInside(code) {
    if (code === 93) {
      effects.consume(code);
      return continuationDeclarationInside;
    }
    return continuation(code);
  }
  function continuationDeclarationInside(code) {
    if (code === 62) {
      effects.consume(code);
      return continuationClose;
    }
    if (code === 45 && marker === 2) {
      effects.consume(code);
      return continuationDeclarationInside;
    }
    return continuation(code);
  }
  function continuationClose(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("htmlFlowData");
      return continuationAfter(code);
    }
    effects.consume(code);
    return continuationClose;
  }
  function continuationAfter(code) {
    effects.exit("htmlFlow");
    return ok2(code);
  }
}
function tokenizeNonLazyContinuationStart(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    if (markdownLineEnding(code)) {
      effects.enter("lineEnding");
      effects.consume(code);
      effects.exit("lineEnding");
      return after;
    }
    return nok(code);
  }
  function after(code) {
    return self.parser.lazy[self.now().line] ? nok(code) : ok2(code);
  }
}
function tokenizeBlankLineBefore(effects, ok2, nok) {
  return start;
  function start(code) {
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return effects.attempt(blankLine, ok2, nok);
  }
}

// node_modules/micromark-core-commonmark/lib/html-text.js
var htmlText = {
  name: "htmlText",
  tokenize: tokenizeHtmlText
};
function tokenizeHtmlText(effects, ok2, nok) {
  const self = this;
  let marker;
  let index2;
  let returnState;
  return start;
  function start(code) {
    effects.enter("htmlText");
    effects.enter("htmlTextData");
    effects.consume(code);
    return open5;
  }
  function open5(code) {
    if (code === 33) {
      effects.consume(code);
      return declarationOpen;
    }
    if (code === 47) {
      effects.consume(code);
      return tagCloseStart;
    }
    if (code === 63) {
      effects.consume(code);
      return instruction;
    }
    if (asciiAlpha(code)) {
      effects.consume(code);
      return tagOpen;
    }
    return nok(code);
  }
  function declarationOpen(code) {
    if (code === 45) {
      effects.consume(code);
      return commentOpenInside;
    }
    if (code === 91) {
      effects.consume(code);
      index2 = 0;
      return cdataOpenInside;
    }
    if (asciiAlpha(code)) {
      effects.consume(code);
      return declaration;
    }
    return nok(code);
  }
  function commentOpenInside(code) {
    if (code === 45) {
      effects.consume(code);
      return commentEnd;
    }
    return nok(code);
  }
  function comment(code) {
    if (code === null) {
      return nok(code);
    }
    if (code === 45) {
      effects.consume(code);
      return commentClose;
    }
    if (markdownLineEnding(code)) {
      returnState = comment;
      return lineEndingBefore(code);
    }
    effects.consume(code);
    return comment;
  }
  function commentClose(code) {
    if (code === 45) {
      effects.consume(code);
      return commentEnd;
    }
    return comment(code);
  }
  function commentEnd(code) {
    return code === 62 ? end(code) : code === 45 ? commentClose(code) : comment(code);
  }
  function cdataOpenInside(code) {
    const value = "CDATA[";
    if (code === value.charCodeAt(index2++)) {
      effects.consume(code);
      return index2 === value.length ? cdata : cdataOpenInside;
    }
    return nok(code);
  }
  function cdata(code) {
    if (code === null) {
      return nok(code);
    }
    if (code === 93) {
      effects.consume(code);
      return cdataClose;
    }
    if (markdownLineEnding(code)) {
      returnState = cdata;
      return lineEndingBefore(code);
    }
    effects.consume(code);
    return cdata;
  }
  function cdataClose(code) {
    if (code === 93) {
      effects.consume(code);
      return cdataEnd;
    }
    return cdata(code);
  }
  function cdataEnd(code) {
    if (code === 62) {
      return end(code);
    }
    if (code === 93) {
      effects.consume(code);
      return cdataEnd;
    }
    return cdata(code);
  }
  function declaration(code) {
    if (code === null || code === 62) {
      return end(code);
    }
    if (markdownLineEnding(code)) {
      returnState = declaration;
      return lineEndingBefore(code);
    }
    effects.consume(code);
    return declaration;
  }
  function instruction(code) {
    if (code === null) {
      return nok(code);
    }
    if (code === 63) {
      effects.consume(code);
      return instructionClose;
    }
    if (markdownLineEnding(code)) {
      returnState = instruction;
      return lineEndingBefore(code);
    }
    effects.consume(code);
    return instruction;
  }
  function instructionClose(code) {
    return code === 62 ? end(code) : instruction(code);
  }
  function tagCloseStart(code) {
    if (asciiAlpha(code)) {
      effects.consume(code);
      return tagClose;
    }
    return nok(code);
  }
  function tagClose(code) {
    if (code === 45 || asciiAlphanumeric(code)) {
      effects.consume(code);
      return tagClose;
    }
    return tagCloseBetween(code);
  }
  function tagCloseBetween(code) {
    if (markdownLineEnding(code)) {
      returnState = tagCloseBetween;
      return lineEndingBefore(code);
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return tagCloseBetween;
    }
    return end(code);
  }
  function tagOpen(code) {
    if (code === 45 || asciiAlphanumeric(code)) {
      effects.consume(code);
      return tagOpen;
    }
    if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
      return tagOpenBetween(code);
    }
    return nok(code);
  }
  function tagOpenBetween(code) {
    if (code === 47) {
      effects.consume(code);
      return end;
    }
    if (code === 58 || code === 95 || asciiAlpha(code)) {
      effects.consume(code);
      return tagOpenAttributeName;
    }
    if (markdownLineEnding(code)) {
      returnState = tagOpenBetween;
      return lineEndingBefore(code);
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return tagOpenBetween;
    }
    return end(code);
  }
  function tagOpenAttributeName(code) {
    if (code === 45 || code === 46 || code === 58 || code === 95 || asciiAlphanumeric(code)) {
      effects.consume(code);
      return tagOpenAttributeName;
    }
    return tagOpenAttributeNameAfter(code);
  }
  function tagOpenAttributeNameAfter(code) {
    if (code === 61) {
      effects.consume(code);
      return tagOpenAttributeValueBefore;
    }
    if (markdownLineEnding(code)) {
      returnState = tagOpenAttributeNameAfter;
      return lineEndingBefore(code);
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return tagOpenAttributeNameAfter;
    }
    return tagOpenBetween(code);
  }
  function tagOpenAttributeValueBefore(code) {
    if (code === null || code === 60 || code === 61 || code === 62 || code === 96) {
      return nok(code);
    }
    if (code === 34 || code === 39) {
      effects.consume(code);
      marker = code;
      return tagOpenAttributeValueQuoted;
    }
    if (markdownLineEnding(code)) {
      returnState = tagOpenAttributeValueBefore;
      return lineEndingBefore(code);
    }
    if (markdownSpace(code)) {
      effects.consume(code);
      return tagOpenAttributeValueBefore;
    }
    effects.consume(code);
    return tagOpenAttributeValueUnquoted;
  }
  function tagOpenAttributeValueQuoted(code) {
    if (code === marker) {
      effects.consume(code);
      marker = void 0;
      return tagOpenAttributeValueQuotedAfter;
    }
    if (code === null) {
      return nok(code);
    }
    if (markdownLineEnding(code)) {
      returnState = tagOpenAttributeValueQuoted;
      return lineEndingBefore(code);
    }
    effects.consume(code);
    return tagOpenAttributeValueQuoted;
  }
  function tagOpenAttributeValueUnquoted(code) {
    if (code === null || code === 34 || code === 39 || code === 60 || code === 61 || code === 96) {
      return nok(code);
    }
    if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
      return tagOpenBetween(code);
    }
    effects.consume(code);
    return tagOpenAttributeValueUnquoted;
  }
  function tagOpenAttributeValueQuotedAfter(code) {
    if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {
      return tagOpenBetween(code);
    }
    return nok(code);
  }
  function end(code) {
    if (code === 62) {
      effects.consume(code);
      effects.exit("htmlTextData");
      effects.exit("htmlText");
      return ok2;
    }
    return nok(code);
  }
  function lineEndingBefore(code) {
    effects.exit("htmlTextData");
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return lineEndingAfter;
  }
  function lineEndingAfter(code) {
    return markdownSpace(code) ? factorySpace(effects, lineEndingAfterPrefix, "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4)(code) : lineEndingAfterPrefix(code);
  }
  function lineEndingAfterPrefix(code) {
    effects.enter("htmlTextData");
    return returnState(code);
  }
}

// node_modules/micromark-core-commonmark/lib/label-end.js
var labelEnd = {
  name: "labelEnd",
  resolveAll: resolveAllLabelEnd,
  resolveTo: resolveToLabelEnd,
  tokenize: tokenizeLabelEnd
};
var resourceConstruct = {
  tokenize: tokenizeResource
};
var referenceFullConstruct = {
  tokenize: tokenizeReferenceFull
};
var referenceCollapsedConstruct = {
  tokenize: tokenizeReferenceCollapsed
};
function resolveAllLabelEnd(events) {
  let index2 = -1;
  const newEvents = [];
  while (++index2 < events.length) {
    const token = events[index2][1];
    newEvents.push(events[index2]);
    if (token.type === "labelImage" || token.type === "labelLink" || token.type === "labelEnd") {
      const offset = token.type === "labelImage" ? 4 : 2;
      token.type = "data";
      index2 += offset;
    }
  }
  if (events.length !== newEvents.length) {
    splice(events, 0, events.length, newEvents);
  }
  return events;
}
function resolveToLabelEnd(events, context) {
  let index2 = events.length;
  let offset = 0;
  let token;
  let open5;
  let close;
  let media;
  while (index2--) {
    token = events[index2][1];
    if (open5) {
      if (token.type === "link" || token.type === "labelLink" && token._inactive) {
        break;
      }
      if (events[index2][0] === "enter" && token.type === "labelLink") {
        token._inactive = true;
      }
    } else if (close) {
      if (events[index2][0] === "enter" && (token.type === "labelImage" || token.type === "labelLink") && !token._balanced) {
        open5 = index2;
        if (token.type !== "labelLink") {
          offset = 2;
          break;
        }
      }
    } else if (token.type === "labelEnd") {
      close = index2;
    }
  }
  const group = {
    type: events[open5][1].type === "labelLink" ? "link" : "image",
    start: {
      ...events[open5][1].start
    },
    end: {
      ...events[events.length - 1][1].end
    }
  };
  const label = {
    type: "label",
    start: {
      ...events[open5][1].start
    },
    end: {
      ...events[close][1].end
    }
  };
  const text3 = {
    type: "labelText",
    start: {
      ...events[open5 + offset + 2][1].end
    },
    end: {
      ...events[close - 2][1].start
    }
  };
  media = [["enter", group, context], ["enter", label, context]];
  media = push(media, events.slice(open5 + 1, open5 + offset + 3));
  media = push(media, [["enter", text3, context]]);
  media = push(media, resolveAll(context.parser.constructs.insideSpan.null, events.slice(open5 + offset + 4, close - 3), context));
  media = push(media, [["exit", text3, context], events[close - 2], events[close - 1], ["exit", label, context]]);
  media = push(media, events.slice(close + 1));
  media = push(media, [["exit", group, context]]);
  splice(events, open5, events.length, media);
  return events;
}
function tokenizeLabelEnd(effects, ok2, nok) {
  const self = this;
  let index2 = self.events.length;
  let labelStart;
  let defined;
  while (index2--) {
    if ((self.events[index2][1].type === "labelImage" || self.events[index2][1].type === "labelLink") && !self.events[index2][1]._balanced) {
      labelStart = self.events[index2][1];
      break;
    }
  }
  return start;
  function start(code) {
    if (!labelStart) {
      return nok(code);
    }
    if (labelStart._inactive) {
      return labelEndNok(code);
    }
    defined = self.parser.defined.includes(normalizeIdentifier(self.sliceSerialize({
      start: labelStart.end,
      end: self.now()
    })));
    effects.enter("labelEnd");
    effects.enter("labelMarker");
    effects.consume(code);
    effects.exit("labelMarker");
    effects.exit("labelEnd");
    return after;
  }
  function after(code) {
    if (code === 40) {
      return effects.attempt(resourceConstruct, labelEndOk, defined ? labelEndOk : labelEndNok)(code);
    }
    if (code === 91) {
      return effects.attempt(referenceFullConstruct, labelEndOk, defined ? referenceNotFull : labelEndNok)(code);
    }
    return defined ? labelEndOk(code) : labelEndNok(code);
  }
  function referenceNotFull(code) {
    return effects.attempt(referenceCollapsedConstruct, labelEndOk, labelEndNok)(code);
  }
  function labelEndOk(code) {
    return ok2(code);
  }
  function labelEndNok(code) {
    labelStart._balanced = true;
    return nok(code);
  }
}
function tokenizeResource(effects, ok2, nok) {
  return resourceStart;
  function resourceStart(code) {
    effects.enter("resource");
    effects.enter("resourceMarker");
    effects.consume(code);
    effects.exit("resourceMarker");
    return resourceBefore;
  }
  function resourceBefore(code) {
    return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceOpen)(code) : resourceOpen(code);
  }
  function resourceOpen(code) {
    if (code === 41) {
      return resourceEnd(code);
    }
    return factoryDestination(effects, resourceDestinationAfter, resourceDestinationMissing, "resourceDestination", "resourceDestinationLiteral", "resourceDestinationLiteralMarker", "resourceDestinationRaw", "resourceDestinationString", 32)(code);
  }
  function resourceDestinationAfter(code) {
    return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
  }
  function resourceDestinationMissing(code) {
    return nok(code);
  }
  function resourceBetween(code) {
    if (code === 34 || code === 39 || code === 40) {
      return factoryTitle(effects, resourceTitleAfter, nok, "resourceTitle", "resourceTitleMarker", "resourceTitleString")(code);
    }
    return resourceEnd(code);
  }
  function resourceTitleAfter(code) {
    return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceEnd)(code) : resourceEnd(code);
  }
  function resourceEnd(code) {
    if (code === 41) {
      effects.enter("resourceMarker");
      effects.consume(code);
      effects.exit("resourceMarker");
      effects.exit("resource");
      return ok2;
    }
    return nok(code);
  }
}
function tokenizeReferenceFull(effects, ok2, nok) {
  const self = this;
  return referenceFull;
  function referenceFull(code) {
    return factoryLabel.call(self, effects, referenceFullAfter, referenceFullMissing, "reference", "referenceMarker", "referenceString")(code);
  }
  function referenceFullAfter(code) {
    return self.parser.defined.includes(normalizeIdentifier(self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1))) ? ok2(code) : nok(code);
  }
  function referenceFullMissing(code) {
    return nok(code);
  }
}
function tokenizeReferenceCollapsed(effects, ok2, nok) {
  return referenceCollapsedStart;
  function referenceCollapsedStart(code) {
    effects.enter("reference");
    effects.enter("referenceMarker");
    effects.consume(code);
    effects.exit("referenceMarker");
    return referenceCollapsedOpen;
  }
  function referenceCollapsedOpen(code) {
    if (code === 93) {
      effects.enter("referenceMarker");
      effects.consume(code);
      effects.exit("referenceMarker");
      effects.exit("reference");
      return ok2;
    }
    return nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/label-start-image.js
var labelStartImage = {
  name: "labelStartImage",
  resolveAll: labelEnd.resolveAll,
  tokenize: tokenizeLabelStartImage
};
function tokenizeLabelStartImage(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    effects.enter("labelImage");
    effects.enter("labelImageMarker");
    effects.consume(code);
    effects.exit("labelImageMarker");
    return open5;
  }
  function open5(code) {
    if (code === 91) {
      effects.enter("labelMarker");
      effects.consume(code);
      effects.exit("labelMarker");
      effects.exit("labelImage");
      return after;
    }
    return nok(code);
  }
  function after(code) {
    return code === 94 && "_hiddenFootnoteSupport" in self.parser.constructs ? nok(code) : ok2(code);
  }
}

// node_modules/micromark-core-commonmark/lib/label-start-link.js
var labelStartLink = {
  name: "labelStartLink",
  resolveAll: labelEnd.resolveAll,
  tokenize: tokenizeLabelStartLink
};
function tokenizeLabelStartLink(effects, ok2, nok) {
  const self = this;
  return start;
  function start(code) {
    effects.enter("labelLink");
    effects.enter("labelMarker");
    effects.consume(code);
    effects.exit("labelMarker");
    effects.exit("labelLink");
    return after;
  }
  function after(code) {
    return code === 94 && "_hiddenFootnoteSupport" in self.parser.constructs ? nok(code) : ok2(code);
  }
}

// node_modules/micromark-core-commonmark/lib/line-ending.js
var lineEnding = {
  name: "lineEnding",
  tokenize: tokenizeLineEnding
};
function tokenizeLineEnding(effects, ok2) {
  return start;
  function start(code) {
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    return factorySpace(effects, ok2, "linePrefix");
  }
}

// node_modules/micromark-core-commonmark/lib/thematic-break.js
var thematicBreak = {
  name: "thematicBreak",
  tokenize: tokenizeThematicBreak
};
function tokenizeThematicBreak(effects, ok2, nok) {
  let size = 0;
  let marker;
  return start;
  function start(code) {
    effects.enter("thematicBreak");
    return before(code);
  }
  function before(code) {
    marker = code;
    return atBreak(code);
  }
  function atBreak(code) {
    if (code === marker) {
      effects.enter("thematicBreakSequence");
      return sequence(code);
    }
    if (size >= 3 && (code === null || markdownLineEnding(code))) {
      effects.exit("thematicBreak");
      return ok2(code);
    }
    return nok(code);
  }
  function sequence(code) {
    if (code === marker) {
      effects.consume(code);
      size++;
      return sequence;
    }
    effects.exit("thematicBreakSequence");
    return markdownSpace(code) ? factorySpace(effects, atBreak, "whitespace")(code) : atBreak(code);
  }
}

// node_modules/micromark-core-commonmark/lib/list.js
var list = {
  continuation: {
    tokenize: tokenizeListContinuation
  },
  exit: tokenizeListEnd,
  name: "list",
  tokenize: tokenizeListStart
};
var listItemPrefixWhitespaceConstruct = {
  partial: true,
  tokenize: tokenizeListItemPrefixWhitespace
};
var indentConstruct = {
  partial: true,
  tokenize: tokenizeIndent
};
function tokenizeListStart(effects, ok2, nok) {
  const self = this;
  const tail = self.events[self.events.length - 1];
  let initialSize = tail && tail[1].type === "linePrefix" ? tail[2].sliceSerialize(tail[1], true).length : 0;
  let size = 0;
  return start;
  function start(code) {
    const kind = self.containerState.type || (code === 42 || code === 43 || code === 45 ? "listUnordered" : "listOrdered");
    if (kind === "listUnordered" ? !self.containerState.marker || code === self.containerState.marker : asciiDigit(code)) {
      if (!self.containerState.type) {
        self.containerState.type = kind;
        effects.enter(kind, {
          _container: true
        });
      }
      if (kind === "listUnordered") {
        effects.enter("listItemPrefix");
        return code === 42 || code === 45 ? effects.check(thematicBreak, nok, atMarker)(code) : atMarker(code);
      }
      if (!self.interrupt || code === 49) {
        effects.enter("listItemPrefix");
        effects.enter("listItemValue");
        return inside(code);
      }
    }
    return nok(code);
  }
  function inside(code) {
    if (asciiDigit(code) && ++size < 10) {
      effects.consume(code);
      return inside;
    }
    if ((!self.interrupt || size < 2) && (self.containerState.marker ? code === self.containerState.marker : code === 41 || code === 46)) {
      effects.exit("listItemValue");
      return atMarker(code);
    }
    return nok(code);
  }
  function atMarker(code) {
    effects.enter("listItemMarker");
    effects.consume(code);
    effects.exit("listItemMarker");
    self.containerState.marker = self.containerState.marker || code;
    return effects.check(
      blankLine,
      // Can’t be empty when interrupting.
      self.interrupt ? nok : onBlank,
      effects.attempt(listItemPrefixWhitespaceConstruct, endOfPrefix, otherPrefix)
    );
  }
  function onBlank(code) {
    self.containerState.initialBlankLine = true;
    initialSize++;
    return endOfPrefix(code);
  }
  function otherPrefix(code) {
    if (markdownSpace(code)) {
      effects.enter("listItemPrefixWhitespace");
      effects.consume(code);
      effects.exit("listItemPrefixWhitespace");
      return endOfPrefix;
    }
    return nok(code);
  }
  function endOfPrefix(code) {
    self.containerState.size = initialSize + self.sliceSerialize(effects.exit("listItemPrefix"), true).length;
    return ok2(code);
  }
}
function tokenizeListContinuation(effects, ok2, nok) {
  const self = this;
  self.containerState._closeFlow = void 0;
  return effects.check(blankLine, onBlank, notBlank);
  function onBlank(code) {
    self.containerState.furtherBlankLines = self.containerState.furtherBlankLines || self.containerState.initialBlankLine;
    return factorySpace(effects, ok2, "listItemIndent", self.containerState.size + 1)(code);
  }
  function notBlank(code) {
    if (self.containerState.furtherBlankLines || !markdownSpace(code)) {
      self.containerState.furtherBlankLines = void 0;
      self.containerState.initialBlankLine = void 0;
      return notInCurrentItem(code);
    }
    self.containerState.furtherBlankLines = void 0;
    self.containerState.initialBlankLine = void 0;
    return effects.attempt(indentConstruct, ok2, notInCurrentItem)(code);
  }
  function notInCurrentItem(code) {
    self.containerState._closeFlow = true;
    self.interrupt = void 0;
    return factorySpace(effects, effects.attempt(list, ok2, nok), "linePrefix", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4)(code);
  }
}
function tokenizeIndent(effects, ok2, nok) {
  const self = this;
  return factorySpace(effects, afterPrefix, "listItemIndent", self.containerState.size + 1);
  function afterPrefix(code) {
    const tail = self.events[self.events.length - 1];
    return tail && tail[1].type === "listItemIndent" && tail[2].sliceSerialize(tail[1], true).length === self.containerState.size ? ok2(code) : nok(code);
  }
}
function tokenizeListEnd(effects) {
  effects.exit(this.containerState.type);
}
function tokenizeListItemPrefixWhitespace(effects, ok2, nok) {
  const self = this;
  return factorySpace(effects, afterPrefix, "listItemPrefixWhitespace", self.parser.constructs.disable.null.includes("codeIndented") ? void 0 : 4 + 1);
  function afterPrefix(code) {
    const tail = self.events[self.events.length - 1];
    return !markdownSpace(code) && tail && tail[1].type === "listItemPrefixWhitespace" ? ok2(code) : nok(code);
  }
}

// node_modules/micromark-core-commonmark/lib/setext-underline.js
var setextUnderline = {
  name: "setextUnderline",
  resolveTo: resolveToSetextUnderline,
  tokenize: tokenizeSetextUnderline
};
function resolveToSetextUnderline(events, context) {
  let index2 = events.length;
  let content3;
  let text3;
  let definition2;
  while (index2--) {
    if (events[index2][0] === "enter") {
      if (events[index2][1].type === "content") {
        content3 = index2;
        break;
      }
      if (events[index2][1].type === "paragraph") {
        text3 = index2;
      }
    } else {
      if (events[index2][1].type === "content") {
        events.splice(index2, 1);
      }
      if (!definition2 && events[index2][1].type === "definition") {
        definition2 = index2;
      }
    }
  }
  const heading = {
    type: "setextHeading",
    start: {
      ...events[content3][1].start
    },
    end: {
      ...events[events.length - 1][1].end
    }
  };
  events[text3][1].type = "setextHeadingText";
  if (definition2) {
    events.splice(text3, 0, ["enter", heading, context]);
    events.splice(definition2 + 1, 0, ["exit", events[content3][1], context]);
    events[content3][1].end = {
      ...events[definition2][1].end
    };
  } else {
    events[content3][1] = heading;
  }
  events.push(["exit", heading, context]);
  return events;
}
function tokenizeSetextUnderline(effects, ok2, nok) {
  const self = this;
  let marker;
  return start;
  function start(code) {
    let index2 = self.events.length;
    let paragraph;
    while (index2--) {
      if (self.events[index2][1].type !== "lineEnding" && self.events[index2][1].type !== "linePrefix" && self.events[index2][1].type !== "content") {
        paragraph = self.events[index2][1].type === "paragraph";
        break;
      }
    }
    if (!self.parser.lazy[self.now().line] && (self.interrupt || paragraph)) {
      effects.enter("setextHeadingLine");
      marker = code;
      return before(code);
    }
    return nok(code);
  }
  function before(code) {
    effects.enter("setextHeadingLineSequence");
    return inside(code);
  }
  function inside(code) {
    if (code === marker) {
      effects.consume(code);
      return inside;
    }
    effects.exit("setextHeadingLineSequence");
    return markdownSpace(code) ? factorySpace(effects, after, "lineSuffix")(code) : after(code);
  }
  function after(code) {
    if (code === null || markdownLineEnding(code)) {
      effects.exit("setextHeadingLine");
      return ok2(code);
    }
    return nok(code);
  }
}

// node_modules/micromark/lib/initialize/flow.js
var flow = {
  tokenize: initializeFlow
};
function initializeFlow(effects) {
  const self = this;
  const initial = effects.attempt(
    // Try to parse a blank line.
    blankLine,
    atBlankEnding,
    // Try to parse initial flow (essentially, only code).
    effects.attempt(this.parser.constructs.flowInitial, afterConstruct, factorySpace(effects, effects.attempt(this.parser.constructs.flow, afterConstruct, effects.attempt(content2, afterConstruct)), "linePrefix"))
  );
  return initial;
  function atBlankEnding(code) {
    if (code === null) {
      effects.consume(code);
      return;
    }
    effects.enter("lineEndingBlank");
    effects.consume(code);
    effects.exit("lineEndingBlank");
    self.currentConstruct = void 0;
    return initial;
  }
  function afterConstruct(code) {
    if (code === null) {
      effects.consume(code);
      return;
    }
    effects.enter("lineEnding");
    effects.consume(code);
    effects.exit("lineEnding");
    self.currentConstruct = void 0;
    return initial;
  }
}

// node_modules/micromark/lib/initialize/text.js
var resolver = {
  resolveAll: createResolver()
};
var string = initializeFactory("string");
var text = initializeFactory("text");
function initializeFactory(field) {
  return {
    resolveAll: createResolver(field === "text" ? resolveAllLineSuffixes : void 0),
    tokenize: initializeText
  };
  function initializeText(effects) {
    const self = this;
    const constructs2 = this.parser.constructs[field];
    const text3 = effects.attempt(constructs2, start, notText);
    return start;
    function start(code) {
      return atBreak(code) ? text3(code) : notText(code);
    }
    function notText(code) {
      if (code === null) {
        effects.consume(code);
        return;
      }
      effects.enter("data");
      effects.consume(code);
      return data;
    }
    function data(code) {
      if (atBreak(code)) {
        effects.exit("data");
        return text3(code);
      }
      effects.consume(code);
      return data;
    }
    function atBreak(code) {
      if (code === null) {
        return true;
      }
      const list2 = constructs2[code];
      let index2 = -1;
      if (list2) {
        while (++index2 < list2.length) {
          const item = list2[index2];
          if (!item.previous || item.previous.call(self, self.previous)) {
            return true;
          }
        }
      }
      return false;
    }
  }
}
function createResolver(extraResolver) {
  return resolveAllText;
  function resolveAllText(events, context) {
    let index2 = -1;
    let enter;
    while (++index2 <= events.length) {
      if (enter === void 0) {
        if (events[index2] && events[index2][1].type === "data") {
          enter = index2;
          index2++;
        }
      } else if (!events[index2] || events[index2][1].type !== "data") {
        if (index2 !== enter + 2) {
          events[enter][1].end = events[index2 - 1][1].end;
          events.splice(enter + 2, index2 - enter - 2);
          index2 = enter + 2;
        }
        enter = void 0;
      }
    }
    return extraResolver ? extraResolver(events, context) : events;
  }
}
function resolveAllLineSuffixes(events, context) {
  let eventIndex = 0;
  while (++eventIndex <= events.length) {
    if ((eventIndex === events.length || events[eventIndex][1].type === "lineEnding") && events[eventIndex - 1][1].type === "data") {
      const data = events[eventIndex - 1][1];
      const chunks = context.sliceStream(data);
      let index2 = chunks.length;
      let bufferIndex = -1;
      let size = 0;
      let tabs;
      while (index2--) {
        const chunk = chunks[index2];
        if (typeof chunk === "string") {
          bufferIndex = chunk.length;
          while (chunk.charCodeAt(bufferIndex - 1) === 32) {
            size++;
            bufferIndex--;
          }
          if (bufferIndex) break;
          bufferIndex = -1;
        } else if (chunk === -2) {
          tabs = true;
          size++;
        } else if (chunk === -1) {
        } else {
          index2++;
          break;
        }
      }
      if (context._contentTypeTextTrailing && eventIndex === events.length) {
        size = 0;
      }
      if (size) {
        const token = {
          type: eventIndex === events.length || tabs || size < 2 ? "lineSuffix" : "hardBreakTrailing",
          start: {
            _bufferIndex: index2 ? bufferIndex : data.start._bufferIndex + bufferIndex,
            _index: data.start._index + index2,
            line: data.end.line,
            column: data.end.column - size,
            offset: data.end.offset - size
          },
          end: {
            ...data.end
          }
        };
        data.end = {
          ...token.start
        };
        if (data.start.offset === data.end.offset) {
          Object.assign(data, token);
        } else {
          events.splice(eventIndex, 0, ["enter", token, context], ["exit", token, context]);
          eventIndex += 2;
        }
      }
      eventIndex++;
    }
  }
  return events;
}

// node_modules/micromark/lib/constructs.js
var constructs_exports = {};
__export(constructs_exports, {
  attentionMarkers: () => attentionMarkers,
  contentInitial: () => contentInitial,
  disable: () => disable,
  document: () => document2,
  flow: () => flow2,
  flowInitial: () => flowInitial,
  insideSpan: () => insideSpan,
  string: () => string2,
  text: () => text2
});
var document2 = {
  [42]: list,
  [43]: list,
  [45]: list,
  [48]: list,
  [49]: list,
  [50]: list,
  [51]: list,
  [52]: list,
  [53]: list,
  [54]: list,
  [55]: list,
  [56]: list,
  [57]: list,
  [62]: blockQuote
};
var contentInitial = {
  [91]: definition
};
var flowInitial = {
  [-2]: codeIndented,
  [-1]: codeIndented,
  [32]: codeIndented
};
var flow2 = {
  [35]: headingAtx,
  [42]: thematicBreak,
  [45]: [setextUnderline, thematicBreak],
  [60]: htmlFlow,
  [61]: setextUnderline,
  [95]: thematicBreak,
  [96]: codeFenced,
  [126]: codeFenced
};
var string2 = {
  [38]: characterReference,
  [92]: characterEscape
};
var text2 = {
  [-5]: lineEnding,
  [-4]: lineEnding,
  [-3]: lineEnding,
  [33]: labelStartImage,
  [38]: characterReference,
  [42]: attention,
  [60]: [autolink, htmlText],
  [91]: labelStartLink,
  [92]: [hardBreakEscape, characterEscape],
  [93]: labelEnd,
  [95]: attention,
  [96]: codeText
};
var insideSpan = {
  null: [attention, resolver]
};
var attentionMarkers = {
  null: [42, 95]
};
var disable = {
  null: []
};

// node_modules/micromark/lib/create-tokenizer.js
function createTokenizer(parser, initialize, from) {
  let point3 = {
    _bufferIndex: -1,
    _index: 0,
    line: from && from.line || 1,
    column: from && from.column || 1,
    offset: from && from.offset || 0
  };
  const columnStart = {};
  const resolveAllConstructs = [];
  let chunks = [];
  let stack = [];
  let consumed = true;
  const effects = {
    attempt: constructFactory(onsuccessfulconstruct),
    check: constructFactory(onsuccessfulcheck),
    consume,
    enter,
    exit: exit2,
    interrupt: constructFactory(onsuccessfulcheck, {
      interrupt: true
    })
  };
  const context = {
    code: null,
    containerState: {},
    defineSkip,
    events: [],
    now,
    parser,
    previous: null,
    sliceSerialize,
    sliceStream,
    write
  };
  let state = initialize.tokenize.call(context, effects);
  let expectedCode;
  if (initialize.resolveAll) {
    resolveAllConstructs.push(initialize);
  }
  return context;
  function write(slice) {
    chunks = push(chunks, slice);
    main2();
    if (chunks[chunks.length - 1] !== null) {
      return [];
    }
    addResult(initialize, 0);
    context.events = resolveAll(resolveAllConstructs, context.events, context);
    return context.events;
  }
  function sliceSerialize(token, expandTabs) {
    return serializeChunks(sliceStream(token), expandTabs);
  }
  function sliceStream(token) {
    return sliceChunks(chunks, token);
  }
  function now() {
    const {
      _bufferIndex,
      _index,
      line,
      column,
      offset
    } = point3;
    return {
      _bufferIndex,
      _index,
      line,
      column,
      offset
    };
  }
  function defineSkip(value) {
    columnStart[value.line] = value.column;
    accountForPotentialSkip();
  }
  function main2() {
    let chunkIndex;
    while (point3._index < chunks.length) {
      const chunk = chunks[point3._index];
      if (typeof chunk === "string") {
        chunkIndex = point3._index;
        if (point3._bufferIndex < 0) {
          point3._bufferIndex = 0;
        }
        while (point3._index === chunkIndex && point3._bufferIndex < chunk.length) {
          go(chunk.charCodeAt(point3._bufferIndex));
        }
      } else {
        go(chunk);
      }
    }
  }
  function go(code) {
    consumed = void 0;
    expectedCode = code;
    state = state(code);
  }
  function consume(code) {
    if (markdownLineEnding(code)) {
      point3.line++;
      point3.column = 1;
      point3.offset += code === -3 ? 2 : 1;
      accountForPotentialSkip();
    } else if (code !== -1) {
      point3.column++;
      point3.offset++;
    }
    if (point3._bufferIndex < 0) {
      point3._index++;
    } else {
      point3._bufferIndex++;
      if (point3._bufferIndex === // Points w/ non-negative `_bufferIndex` reference
      // strings.
      /** @type {string} */
      chunks[point3._index].length) {
        point3._bufferIndex = -1;
        point3._index++;
      }
    }
    context.previous = code;
    consumed = true;
  }
  function enter(type, fields) {
    const token = fields || {};
    token.type = type;
    token.start = now();
    context.events.push(["enter", token, context]);
    stack.push(token);
    return token;
  }
  function exit2(type) {
    const token = stack.pop();
    token.end = now();
    context.events.push(["exit", token, context]);
    return token;
  }
  function onsuccessfulconstruct(construct, info) {
    addResult(construct, info.from);
  }
  function onsuccessfulcheck(_, info) {
    info.restore();
  }
  function constructFactory(onreturn, fields) {
    return hook;
    function hook(constructs2, returnState, bogusState) {
      let listOfConstructs;
      let constructIndex;
      let currentConstruct;
      let info;
      return Array.isArray(constructs2) ? (
        /* c8 ignore next 1 */
        handleListOfConstructs(constructs2)
      ) : "tokenize" in constructs2 ? (
        // Looks like a construct.
        handleListOfConstructs([
          /** @type {Construct} */
          constructs2
        ])
      ) : handleMapOfConstructs(constructs2);
      function handleMapOfConstructs(map) {
        return start;
        function start(code) {
          const left = code !== null && map[code];
          const all2 = code !== null && map.null;
          const list2 = [
            // To do: add more extension tests.
            /* c8 ignore next 2 */
            ...Array.isArray(left) ? left : left ? [left] : [],
            ...Array.isArray(all2) ? all2 : all2 ? [all2] : []
          ];
          return handleListOfConstructs(list2)(code);
        }
      }
      function handleListOfConstructs(list2) {
        listOfConstructs = list2;
        constructIndex = 0;
        if (list2.length === 0) {
          return bogusState;
        }
        return handleConstruct(list2[constructIndex]);
      }
      function handleConstruct(construct) {
        return start;
        function start(code) {
          info = store();
          currentConstruct = construct;
          if (!construct.partial) {
            context.currentConstruct = construct;
          }
          if (construct.name && context.parser.constructs.disable.null.includes(construct.name)) {
            return nok(code);
          }
          return construct.tokenize.call(
            // If we do have fields, create an object w/ `context` as its
            // prototype.
            // This allows a “live binding”, which is needed for `interrupt`.
            fields ? Object.assign(Object.create(context), fields) : context,
            effects,
            ok2,
            nok
          )(code);
        }
      }
      function ok2(code) {
        consumed = true;
        onreturn(currentConstruct, info);
        return returnState;
      }
      function nok(code) {
        consumed = true;
        info.restore();
        if (++constructIndex < listOfConstructs.length) {
          return handleConstruct(listOfConstructs[constructIndex]);
        }
        return bogusState;
      }
    }
  }
  function addResult(construct, from2) {
    if (construct.resolveAll && !resolveAllConstructs.includes(construct)) {
      resolveAllConstructs.push(construct);
    }
    if (construct.resolve) {
      splice(context.events, from2, context.events.length - from2, construct.resolve(context.events.slice(from2), context));
    }
    if (construct.resolveTo) {
      context.events = construct.resolveTo(context.events, context);
    }
  }
  function store() {
    const startPoint = now();
    const startPrevious = context.previous;
    const startCurrentConstruct = context.currentConstruct;
    const startEventsIndex = context.events.length;
    const startStack = Array.from(stack);
    return {
      from: startEventsIndex,
      restore
    };
    function restore() {
      point3 = startPoint;
      context.previous = startPrevious;
      context.currentConstruct = startCurrentConstruct;
      context.events.length = startEventsIndex;
      stack = startStack;
      accountForPotentialSkip();
    }
  }
  function accountForPotentialSkip() {
    if (point3.line in columnStart && point3.column < 2) {
      point3.column = columnStart[point3.line];
      point3.offset += columnStart[point3.line] - 1;
    }
  }
}
function sliceChunks(chunks, token) {
  const startIndex = token.start._index;
  const startBufferIndex = token.start._bufferIndex;
  const endIndex = token.end._index;
  const endBufferIndex = token.end._bufferIndex;
  let view;
  if (startIndex === endIndex) {
    view = [chunks[startIndex].slice(startBufferIndex, endBufferIndex)];
  } else {
    view = chunks.slice(startIndex, endIndex);
    if (startBufferIndex > -1) {
      const head = view[0];
      if (typeof head === "string") {
        view[0] = head.slice(startBufferIndex);
      } else {
        view.shift();
      }
    }
    if (endBufferIndex > 0) {
      view.push(chunks[endIndex].slice(0, endBufferIndex));
    }
  }
  return view;
}
function serializeChunks(chunks, expandTabs) {
  let index2 = -1;
  const result = [];
  let atTab;
  while (++index2 < chunks.length) {
    const chunk = chunks[index2];
    let value;
    if (typeof chunk === "string") {
      value = chunk;
    } else switch (chunk) {
      case -5: {
        value = "\r";
        break;
      }
      case -4: {
        value = "\n";
        break;
      }
      case -3: {
        value = "\r\n";
        break;
      }
      case -2: {
        value = expandTabs ? " " : "	";
        break;
      }
      case -1: {
        if (!expandTabs && atTab) continue;
        value = " ";
        break;
      }
      default: {
        value = String.fromCharCode(chunk);
      }
    }
    atTab = chunk === -2;
    result.push(value);
  }
  return result.join("");
}

// node_modules/micromark/lib/parse.js
function parse(options) {
  const settings = options || {};
  const constructs2 = (
    /** @type {FullNormalizedExtension} */
    combineExtensions([constructs_exports, ...settings.extensions || []])
  );
  const parser = {
    constructs: constructs2,
    content: create(content),
    defined: [],
    document: create(document),
    flow: create(flow),
    lazy: {},
    string: create(string),
    text: create(text)
  };
  return parser;
  function create(initial) {
    return creator;
    function creator(from) {
      return createTokenizer(parser, initial, from);
    }
  }
}

// node_modules/micromark/lib/postprocess.js
function postprocess(events) {
  while (!subtokenize(events)) {
  }
  return events;
}

// node_modules/micromark/lib/preprocess.js
var search = /[\0\t\n\r]/g;
function preprocess() {
  let column = 1;
  let buffer = "";
  let start = true;
  let atCarriageReturn;
  return preprocessor;
  function preprocessor(value, encoding, end) {
    const chunks = [];
    let match;
    let next;
    let startPosition;
    let endPosition;
    let code;
    value = buffer + (typeof value === "string" ? value.toString() : new TextDecoder(encoding || void 0).decode(value));
    startPosition = 0;
    buffer = "";
    if (start) {
      if (value.charCodeAt(0) === 65279) {
        startPosition++;
      }
      start = void 0;
    }
    while (startPosition < value.length) {
      search.lastIndex = startPosition;
      match = search.exec(value);
      endPosition = match && match.index !== void 0 ? match.index : value.length;
      code = value.charCodeAt(endPosition);
      if (!match) {
        buffer = value.slice(startPosition);
        break;
      }
      if (code === 10 && startPosition === endPosition && atCarriageReturn) {
        chunks.push(-3);
        atCarriageReturn = void 0;
      } else {
        if (atCarriageReturn) {
          chunks.push(-5);
          atCarriageReturn = void 0;
        }
        if (startPosition < endPosition) {
          chunks.push(value.slice(startPosition, endPosition));
          column += endPosition - startPosition;
        }
        switch (code) {
          case 0: {
            chunks.push(65533);
            column++;
            break;
          }
          case 9: {
            next = Math.ceil(column / 4) * 4;
            chunks.push(-2);
            while (column++ < next) chunks.push(-1);
            break;
          }
          case 10: {
            chunks.push(-4);
            column = 1;
            break;
          }
          default: {
            atCarriageReturn = true;
            column = 1;
          }
        }
      }
      startPosition = endPosition + 1;
    }
    if (end) {
      if (atCarriageReturn) chunks.push(-5);
      if (buffer) chunks.push(buffer);
      chunks.push(null);
    }
    return chunks;
  }
}

// node_modules/micromark-util-decode-string/index.js
var characterEscapeOrReference = /\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;
function decodeString(value) {
  return value.replace(characterEscapeOrReference, decode);
}
function decode($0, $1, $2) {
  if ($1) {
    return $1;
  }
  const head = $2.charCodeAt(0);
  if (head === 35) {
    const head2 = $2.charCodeAt(1);
    const hex = head2 === 120 || head2 === 88;
    return decodeNumericCharacterReference($2.slice(hex ? 2 : 1), hex ? 16 : 10);
  }
  return decodeNamedCharacterReference($2) || $0;
}

// node_modules/unist-util-stringify-position/lib/index.js
function stringifyPosition(value) {
  if (!value || typeof value !== "object") {
    return "";
  }
  if ("position" in value || "type" in value) {
    return position(value.position);
  }
  if ("start" in value || "end" in value) {
    return position(value);
  }
  if ("line" in value || "column" in value) {
    return point(value);
  }
  return "";
}
function point(point3) {
  return index(point3 && point3.line) + ":" + index(point3 && point3.column);
}
function position(pos) {
  return point(pos && pos.start) + "-" + point(pos && pos.end);
}
function index(value) {
  return value && typeof value === "number" ? value : 1;
}

// node_modules/mdast-util-from-markdown/lib/index.js
var own2 = {}.hasOwnProperty;
function fromMarkdown(value, encoding, options) {
  if (encoding && typeof encoding === "object") {
    options = encoding;
    encoding = void 0;
  }
  return compiler(options)(postprocess(parse(options).document().write(preprocess()(value, encoding, true))));
}
function compiler(options) {
  const config = {
    transforms: [],
    canContainEols: ["emphasis", "fragment", "heading", "paragraph", "strong"],
    enter: {
      autolink: opener(link3),
      autolinkProtocol: onenterdata,
      autolinkEmail: onenterdata,
      atxHeading: opener(heading),
      blockQuote: opener(blockQuote2),
      characterEscape: onenterdata,
      characterReference: onenterdata,
      codeFenced: opener(codeFlow),
      codeFencedFenceInfo: buffer,
      codeFencedFenceMeta: buffer,
      codeIndented: opener(codeFlow, buffer),
      codeText: opener(codeText2, buffer),
      codeTextData: onenterdata,
      data: onenterdata,
      codeFlowValue: onenterdata,
      definition: opener(definition2),
      definitionDestinationString: buffer,
      definitionLabelString: buffer,
      definitionTitleString: buffer,
      emphasis: opener(emphasis),
      hardBreakEscape: opener(hardBreak),
      hardBreakTrailing: opener(hardBreak),
      htmlFlow: opener(html, buffer),
      htmlFlowData: onenterdata,
      htmlText: opener(html, buffer),
      htmlTextData: onenterdata,
      image: opener(image),
      label: buffer,
      link: opener(link3),
      listItem: opener(listItem),
      listItemValue: onenterlistitemvalue,
      listOrdered: opener(list2, onenterlistordered),
      listUnordered: opener(list2),
      paragraph: opener(paragraph),
      reference: onenterreference,
      referenceString: buffer,
      resourceDestinationString: buffer,
      resourceTitleString: buffer,
      setextHeading: opener(heading),
      strong: opener(strong),
      thematicBreak: opener(thematicBreak2)
    },
    exit: {
      atxHeading: closer(),
      atxHeadingSequence: onexitatxheadingsequence,
      autolink: closer(),
      autolinkEmail: onexitautolinkemail,
      autolinkProtocol: onexitautolinkprotocol,
      blockQuote: closer(),
      characterEscapeValue: onexitdata,
      characterReferenceMarkerHexadecimal: onexitcharacterreferencemarker,
      characterReferenceMarkerNumeric: onexitcharacterreferencemarker,
      characterReferenceValue: onexitcharacterreferencevalue,
      characterReference: onexitcharacterreference,
      codeFenced: closer(onexitcodefenced),
      codeFencedFence: onexitcodefencedfence,
      codeFencedFenceInfo: onexitcodefencedfenceinfo,
      codeFencedFenceMeta: onexitcodefencedfencemeta,
      codeFlowValue: onexitdata,
      codeIndented: closer(onexitcodeindented),
      codeText: closer(onexitcodetext),
      codeTextData: onexitdata,
      data: onexitdata,
      definition: closer(),
      definitionDestinationString: onexitdefinitiondestinationstring,
      definitionLabelString: onexitdefinitionlabelstring,
      definitionTitleString: onexitdefinitiontitlestring,
      emphasis: closer(),
      hardBreakEscape: closer(onexithardbreak),
      hardBreakTrailing: closer(onexithardbreak),
      htmlFlow: closer(onexithtmlflow),
      htmlFlowData: onexitdata,
      htmlText: closer(onexithtmltext),
      htmlTextData: onexitdata,
      image: closer(onexitimage),
      label: onexitlabel,
      labelText: onexitlabeltext,
      lineEnding: onexitlineending,
      link: closer(onexitlink),
      listItem: closer(),
      listOrdered: closer(),
      listUnordered: closer(),
      paragraph: closer(),
      referenceString: onexitreferencestring,
      resourceDestinationString: onexitresourcedestinationstring,
      resourceTitleString: onexitresourcetitlestring,
      resource: onexitresource,
      setextHeading: closer(onexitsetextheading),
      setextHeadingLineSequence: onexitsetextheadinglinesequence,
      setextHeadingText: onexitsetextheadingtext,
      strong: closer(),
      thematicBreak: closer()
    }
  };
  configure(config, (options || {}).mdastExtensions || []);
  const data = {};
  return compile;
  function compile(events) {
    let tree = {
      type: "root",
      children: []
    };
    const context = {
      stack: [tree],
      tokenStack: [],
      config,
      enter,
      exit: exit2,
      buffer,
      resume,
      data
    };
    const listStack = [];
    let index2 = -1;
    while (++index2 < events.length) {
      if (events[index2][1].type === "listOrdered" || events[index2][1].type === "listUnordered") {
        if (events[index2][0] === "enter") {
          listStack.push(index2);
        } else {
          const tail = listStack.pop();
          index2 = prepareList(events, tail, index2);
        }
      }
    }
    index2 = -1;
    while (++index2 < events.length) {
      const handler = config[events[index2][0]];
      if (own2.call(handler, events[index2][1].type)) {
        handler[events[index2][1].type].call(Object.assign({
          sliceSerialize: events[index2][2].sliceSerialize
        }, context), events[index2][1]);
      }
    }
    if (context.tokenStack.length > 0) {
      const tail = context.tokenStack[context.tokenStack.length - 1];
      const handler = tail[1] || defaultOnError;
      handler.call(context, void 0, tail[0]);
    }
    tree.position = {
      start: point2(events.length > 0 ? events[0][1].start : {
        line: 1,
        column: 1,
        offset: 0
      }),
      end: point2(events.length > 0 ? events[events.length - 2][1].end : {
        line: 1,
        column: 1,
        offset: 0
      })
    };
    index2 = -1;
    while (++index2 < config.transforms.length) {
      tree = config.transforms[index2](tree) || tree;
    }
    return tree;
  }
  function prepareList(events, start, length) {
    let index2 = start - 1;
    let containerBalance = -1;
    let listSpread = false;
    let listItem2;
    let lineIndex;
    let firstBlankLineIndex;
    let atMarker;
    while (++index2 <= length) {
      const event = events[index2];
      switch (event[1].type) {
        case "listUnordered":
        case "listOrdered":
        case "blockQuote": {
          if (event[0] === "enter") {
            containerBalance++;
          } else {
            containerBalance--;
          }
          atMarker = void 0;
          break;
        }
        case "lineEndingBlank": {
          if (event[0] === "enter") {
            if (listItem2 && !atMarker && !containerBalance && !firstBlankLineIndex) {
              firstBlankLineIndex = index2;
            }
            atMarker = void 0;
          }
          break;
        }
        case "linePrefix":
        case "listItemValue":
        case "listItemMarker":
        case "listItemPrefix":
        case "listItemPrefixWhitespace": {
          break;
        }
        default: {
          atMarker = void 0;
        }
      }
      if (!containerBalance && event[0] === "enter" && event[1].type === "listItemPrefix" || containerBalance === -1 && event[0] === "exit" && (event[1].type === "listUnordered" || event[1].type === "listOrdered")) {
        if (listItem2) {
          let tailIndex = index2;
          lineIndex = void 0;
          while (tailIndex--) {
            const tailEvent = events[tailIndex];
            if (tailEvent[1].type === "lineEnding" || tailEvent[1].type === "lineEndingBlank") {
              if (tailEvent[0] === "exit") continue;
              if (lineIndex) {
                events[lineIndex][1].type = "lineEndingBlank";
                listSpread = true;
              }
              tailEvent[1].type = "lineEnding";
              lineIndex = tailIndex;
            } else if (tailEvent[1].type === "linePrefix" || tailEvent[1].type === "blockQuotePrefix" || tailEvent[1].type === "blockQuotePrefixWhitespace" || tailEvent[1].type === "blockQuoteMarker" || tailEvent[1].type === "listItemIndent") {
            } else {
              break;
            }
          }
          if (firstBlankLineIndex && (!lineIndex || firstBlankLineIndex < lineIndex)) {
            listItem2._spread = true;
          }
          listItem2.end = Object.assign({}, lineIndex ? events[lineIndex][1].start : event[1].end);
          events.splice(lineIndex || index2, 0, ["exit", listItem2, event[2]]);
          index2++;
          length++;
        }
        if (event[1].type === "listItemPrefix") {
          const item = {
            type: "listItem",
            _spread: false,
            start: Object.assign({}, event[1].start),
            // @ts-expect-error: we’ll add `end` in a second.
            end: void 0
          };
          listItem2 = item;
          events.splice(index2, 0, ["enter", item, event[2]]);
          index2++;
          length++;
          firstBlankLineIndex = void 0;
          atMarker = true;
        }
      }
    }
    events[start][1]._spread = listSpread;
    return length;
  }
  function opener(create, and) {
    return open5;
    function open5(token) {
      enter.call(this, create(token), token);
      if (and) and.call(this, token);
    }
  }
  function buffer() {
    this.stack.push({
      type: "fragment",
      children: []
    });
  }
  function enter(node2, token, errorHandler) {
    const parent = this.stack[this.stack.length - 1];
    const siblings = parent.children;
    siblings.push(node2);
    this.stack.push(node2);
    this.tokenStack.push([token, errorHandler || void 0]);
    node2.position = {
      start: point2(token.start),
      // @ts-expect-error: `end` will be patched later.
      end: void 0
    };
  }
  function closer(and) {
    return close;
    function close(token) {
      if (and) and.call(this, token);
      exit2.call(this, token);
    }
  }
  function exit2(token, onExitError) {
    const node2 = this.stack.pop();
    const open5 = this.tokenStack.pop();
    if (!open5) {
      throw new Error("Cannot close `" + token.type + "` (" + stringifyPosition({
        start: token.start,
        end: token.end
      }) + "): it\u2019s not open");
    } else if (open5[0].type !== token.type) {
      if (onExitError) {
        onExitError.call(this, token, open5[0]);
      } else {
        const handler = open5[1] || defaultOnError;
        handler.call(this, token, open5[0]);
      }
    }
    node2.position.end = point2(token.end);
  }
  function resume() {
    return toString(this.stack.pop());
  }
  function onenterlistordered() {
    this.data.expectingFirstListItemValue = true;
  }
  function onenterlistitemvalue(token) {
    if (this.data.expectingFirstListItemValue) {
      const ancestor = this.stack[this.stack.length - 2];
      ancestor.start = Number.parseInt(this.sliceSerialize(token), 10);
      this.data.expectingFirstListItemValue = void 0;
    }
  }
  function onexitcodefencedfenceinfo() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.lang = data2;
  }
  function onexitcodefencedfencemeta() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.meta = data2;
  }
  function onexitcodefencedfence() {
    if (this.data.flowCodeInside) return;
    this.buffer();
    this.data.flowCodeInside = true;
  }
  function onexitcodefenced() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.value = data2.replace(/^(\r?\n|\r)|(\r?\n|\r)$/g, "");
    this.data.flowCodeInside = void 0;
  }
  function onexitcodeindented() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.value = data2.replace(/(\r?\n|\r)$/g, "");
  }
  function onexitdefinitionlabelstring(token) {
    const label = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.label = label;
    node2.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
  }
  function onexitdefinitiontitlestring() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.title = data2;
  }
  function onexitdefinitiondestinationstring() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.url = data2;
  }
  function onexitatxheadingsequence(token) {
    const node2 = this.stack[this.stack.length - 1];
    if (!node2.depth) {
      const depth = this.sliceSerialize(token).length;
      node2.depth = depth;
    }
  }
  function onexitsetextheadingtext() {
    this.data.setextHeadingSlurpLineEnding = true;
  }
  function onexitsetextheadinglinesequence(token) {
    const node2 = this.stack[this.stack.length - 1];
    node2.depth = this.sliceSerialize(token).codePointAt(0) === 61 ? 1 : 2;
  }
  function onexitsetextheading() {
    this.data.setextHeadingSlurpLineEnding = void 0;
  }
  function onenterdata(token) {
    const node2 = this.stack[this.stack.length - 1];
    const siblings = node2.children;
    let tail = siblings[siblings.length - 1];
    if (!tail || tail.type !== "text") {
      tail = text3();
      tail.position = {
        start: point2(token.start),
        // @ts-expect-error: we’ll add `end` later.
        end: void 0
      };
      siblings.push(tail);
    }
    this.stack.push(tail);
  }
  function onexitdata(token) {
    const tail = this.stack.pop();
    tail.value += this.sliceSerialize(token);
    tail.position.end = point2(token.end);
  }
  function onexitlineending(token) {
    const context = this.stack[this.stack.length - 1];
    if (this.data.atHardBreak) {
      const tail = context.children[context.children.length - 1];
      tail.position.end = point2(token.end);
      this.data.atHardBreak = void 0;
      return;
    }
    if (!this.data.setextHeadingSlurpLineEnding && config.canContainEols.includes(context.type)) {
      onenterdata.call(this, token);
      onexitdata.call(this, token);
    }
  }
  function onexithardbreak() {
    this.data.atHardBreak = true;
  }
  function onexithtmlflow() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.value = data2;
  }
  function onexithtmltext() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.value = data2;
  }
  function onexitcodetext() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.value = data2;
  }
  function onexitlink() {
    const node2 = this.stack[this.stack.length - 1];
    if (this.data.inReference) {
      const referenceType = this.data.referenceType || "shortcut";
      node2.type += "Reference";
      node2.referenceType = referenceType;
      delete node2.url;
      delete node2.title;
    } else {
      delete node2.identifier;
      delete node2.label;
    }
    this.data.referenceType = void 0;
  }
  function onexitimage() {
    const node2 = this.stack[this.stack.length - 1];
    if (this.data.inReference) {
      const referenceType = this.data.referenceType || "shortcut";
      node2.type += "Reference";
      node2.referenceType = referenceType;
      delete node2.url;
      delete node2.title;
    } else {
      delete node2.identifier;
      delete node2.label;
    }
    this.data.referenceType = void 0;
  }
  function onexitlabeltext(token) {
    const string3 = this.sliceSerialize(token);
    const ancestor = this.stack[this.stack.length - 2];
    ancestor.label = decodeString(string3);
    ancestor.identifier = normalizeIdentifier(string3).toLowerCase();
  }
  function onexitlabel() {
    const fragment = this.stack[this.stack.length - 1];
    const value = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    this.data.inReference = true;
    if (node2.type === "link") {
      const children = fragment.children;
      node2.children = children;
    } else {
      node2.alt = value;
    }
  }
  function onexitresourcedestinationstring() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.url = data2;
  }
  function onexitresourcetitlestring() {
    const data2 = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.title = data2;
  }
  function onexitresource() {
    this.data.inReference = void 0;
  }
  function onenterreference() {
    this.data.referenceType = "collapsed";
  }
  function onexitreferencestring(token) {
    const label = this.resume();
    const node2 = this.stack[this.stack.length - 1];
    node2.label = label;
    node2.identifier = normalizeIdentifier(this.sliceSerialize(token)).toLowerCase();
    this.data.referenceType = "full";
  }
  function onexitcharacterreferencemarker(token) {
    this.data.characterReferenceType = token.type;
  }
  function onexitcharacterreferencevalue(token) {
    const data2 = this.sliceSerialize(token);
    const type = this.data.characterReferenceType;
    let value;
    if (type) {
      value = decodeNumericCharacterReference(data2, type === "characterReferenceMarkerNumeric" ? 10 : 16);
      this.data.characterReferenceType = void 0;
    } else {
      const result = decodeNamedCharacterReference(data2);
      value = result;
    }
    const tail = this.stack[this.stack.length - 1];
    tail.value += value;
  }
  function onexitcharacterreference(token) {
    const tail = this.stack.pop();
    tail.position.end = point2(token.end);
  }
  function onexitautolinkprotocol(token) {
    onexitdata.call(this, token);
    const node2 = this.stack[this.stack.length - 1];
    node2.url = this.sliceSerialize(token);
  }
  function onexitautolinkemail(token) {
    onexitdata.call(this, token);
    const node2 = this.stack[this.stack.length - 1];
    node2.url = "mailto:" + this.sliceSerialize(token);
  }
  function blockQuote2() {
    return {
      type: "blockquote",
      children: []
    };
  }
  function codeFlow() {
    return {
      type: "code",
      lang: null,
      meta: null,
      value: ""
    };
  }
  function codeText2() {
    return {
      type: "inlineCode",
      value: ""
    };
  }
  function definition2() {
    return {
      type: "definition",
      identifier: "",
      label: null,
      title: null,
      url: ""
    };
  }
  function emphasis() {
    return {
      type: "emphasis",
      children: []
    };
  }
  function heading() {
    return {
      type: "heading",
      // @ts-expect-error `depth` will be set later.
      depth: 0,
      children: []
    };
  }
  function hardBreak() {
    return {
      type: "break"
    };
  }
  function html() {
    return {
      type: "html",
      value: ""
    };
  }
  function image() {
    return {
      type: "image",
      title: null,
      url: "",
      alt: null
    };
  }
  function link3() {
    return {
      type: "link",
      title: null,
      url: "",
      children: []
    };
  }
  function list2(token) {
    return {
      type: "list",
      ordered: token.type === "listOrdered",
      start: null,
      spread: token._spread,
      children: []
    };
  }
  function listItem(token) {
    return {
      type: "listItem",
      spread: token._spread,
      checked: null,
      children: []
    };
  }
  function paragraph() {
    return {
      type: "paragraph",
      children: []
    };
  }
  function strong() {
    return {
      type: "strong",
      children: []
    };
  }
  function text3() {
    return {
      type: "text",
      value: ""
    };
  }
  function thematicBreak2() {
    return {
      type: "thematicBreak"
    };
  }
}
function point2(d) {
  return {
    line: d.line,
    column: d.column,
    offset: d.offset
  };
}
function configure(combined, extensions) {
  let index2 = -1;
  while (++index2 < extensions.length) {
    const value = extensions[index2];
    if (Array.isArray(value)) {
      configure(combined, value);
    } else {
      extension(combined, value);
    }
  }
}
function extension(combined, extension2) {
  let key2;
  for (key2 in extension2) {
    if (own2.call(extension2, key2)) {
      switch (key2) {
        case "canContainEols": {
          const right = extension2[key2];
          if (right) {
            combined[key2].push(...right);
          }
          break;
        }
        case "transforms": {
          const right = extension2[key2];
          if (right) {
            combined[key2].push(...right);
          }
          break;
        }
        case "enter":
        case "exit": {
          const right = extension2[key2];
          if (right) {
            Object.assign(combined[key2], right);
          }
          break;
        }
      }
    }
  }
}
function defaultOnError(left, right) {
  if (left) {
    throw new Error("Cannot close `" + left.type + "` (" + stringifyPosition({
      start: left.start,
      end: left.end
    }) + "): a different token (`" + right.type + "`, " + stringifyPosition({
      start: right.start,
      end: right.end
    }) + ") is open");
  } else {
    throw new Error("Cannot close document, a token (`" + right.type + "`, " + stringifyPosition({
      start: right.start,
      end: right.end
    }) + ") is still open");
  }
}

// src/adapters/acp/image-prompt.ts
var MAX_ACP_IMAGE_BYTES = MAX_ATTACHMENT_BYTES;
var MAX_ACP_IMAGES_PER_PROMPT = 8;
var MAX_ACP_IMAGES_TOTAL_BYTES = MAX_ATTACHMENT_BYTES;
var ACP_IMAGE_MIME_BY_EXT = {
  ".png": "image/png",
  ".jpg": "image/jpeg",
  ".jpeg": "image/jpeg",
  ".gif": "image/gif",
  ".webp": "image/webp",
  ".bmp": "image/bmp"
};
function extractMarkdownImageRefs(body, options) {
  if (!body || !body.trim()) return [];
  const tree = fromMarkdown(body);
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  const fromNotePath = options?.fromNotePath;
  walkMdast(tree, (node2) => {
    if (node2.type !== "image") return;
    const img = node2;
    const rawUrl = (img.url ?? "").trim();
    if (!rawUrl) return;
    if (isExternalOrDataUrl(rawUrl)) return;
    const resolved = resolveLocalImagePath(rawUrl, fromNotePath);
    if (!resolved) return;
    const key2 = resolved.toLowerCase();
    if (seen.has(key2)) return;
    seen.add(key2);
    const alt = typeof img.alt === "string" && img.alt.trim() ? img.alt.trim() : void 0;
    const markdownPointer = alt ? `![${alt}](${rawUrl})` : `![](${rawUrl})`;
    out.push({
      relativePath: resolved,
      ...alt ? { alt } : {},
      markdownPointer
    });
  });
  return out;
}
async function projectBootstrapImagesToAcpPrompt(input) {
  const bootstrapText = input.bootstrapText ?? "";
  const refs = Array.isArray(input.imageRefs) ? input.imageRefs : [];
  const transportOk = input.transportSupportsImage === true;
  const notes = [];
  const fallbackPointers = [];
  if (refs.length === 0) {
    return {
      prompt: [{ type: "text", text: bootstrapText }],
      imagesAttached: false,
      fallbackPointers: [],
      notes: []
    };
  }
  if (!transportOk) {
    for (const ref of refs) {
      const ptr = ref.markdownPointer || ref.relativePath;
      if (ptr) fallbackPointers.push(ptr);
    }
    const reason = "ACP transport did not advertise promptCapabilities.image (treated as unsupported)";
    notes.push(reason);
    const text4 = appendImageFallbackNote(bootstrapText, fallbackPointers, reason);
    return {
      prompt: [{ type: "text", text: text4 }],
      imagesAttached: false,
      fallbackPointers,
      notes
    };
  }
  if (typeof input.readBinaryBounded !== "function") {
    notes.push("bounded image reader unavailable; keeping Markdown image pointers");
    for (const ref of refs) {
      fallbackPointers.push(ref.markdownPointer || ref.relativePath);
    }
    const text4 = appendImageFallbackNote(
      bootstrapText,
      fallbackPointers,
      "image bytes unavailable"
    );
    return {
      prompt: [{ type: "text", text: text4 }],
      imagesAttached: false,
      fallbackPointers,
      notes
    };
  }
  const imageBlocks = [];
  const limited = refs.slice(0, MAX_ACP_IMAGES_PER_PROMPT);
  if (refs.length > MAX_ACP_IMAGES_PER_PROMPT) {
    notes.push(
      `image cap: projecting first ${MAX_ACP_IMAGES_PER_PROMPT} of ${refs.length} refs`
    );
    for (const ref of refs.slice(MAX_ACP_IMAGES_PER_PROMPT)) {
      fallbackPointers.push(ref.markdownPointer || ref.relativePath);
    }
  }
  let totalBytes = 0;
  for (const ref of limited) {
    const rel = normalizeSystemRelativePath(ref.relativePath);
    if (!rel) {
      fallbackPointers.push(ref.markdownPointer || ref.relativePath);
      notes.push(`skip unsafe path: ${ref.relativePath}`);
      continue;
    }
    const extMime = mimeFromPath(rel);
    if (!extMime) {
      fallbackPointers.push(ref.markdownPointer || rel);
      notes.push(`skip unsupported image type: ${rel}`);
      continue;
    }
    const remainingTotalBytes = MAX_ACP_IMAGES_TOTAL_BYTES - totalBytes;
    const allowedBytes = Math.min(MAX_ACP_IMAGE_BYTES, remainingTotalBytes);
    let read;
    try {
      read = await input.readBinaryBounded(rel, allowedBytes + 1);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      fallbackPointers.push(ref.markdownPointer || rel);
      notes.push(`skip unreadable image ${rel}: ${msg.slice(0, 120)}`);
      continue;
    }
    const { bytes } = read;
    if (read.truncated || bytes.byteLength > allowedBytes) {
      fallbackPointers.push(ref.markdownPointer || rel);
      if (remainingTotalBytes < MAX_ACP_IMAGE_BYTES) {
        notes.push(
          `skip image ${rel}: would exceed total budget ${MAX_ACP_IMAGES_TOTAL_BYTES}`
        );
      } else {
        notes.push(`skip oversized image ${rel}: exceeds ${MAX_ACP_IMAGE_BYTES}`);
      }
      continue;
    }
    if (bytes.byteLength <= 0) {
      fallbackPointers.push(ref.markdownPointer || rel);
      notes.push(`skip empty image: ${rel}`);
      continue;
    }
    const sniffed = sniffImageMime(bytes);
    if (!sniffed || !isAllowedImageMime(sniffed)) {
      fallbackPointers.push(ref.markdownPointer || rel);
      notes.push(`skip non-image or magic-mismatch bytes: ${rel}`);
      continue;
    }
    if (!mimeExtAgrees(extMime, sniffed)) {
      fallbackPointers.push(ref.markdownPointer || rel);
      notes.push(`skip extension/magic mismatch: ${rel} (${extMime} vs ${sniffed})`);
      continue;
    }
    totalBytes += bytes.byteLength;
    const block = {
      type: "image",
      data: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString(
        "base64"
      ),
      mimeType: sniffed
    };
    const uri = fileUriForSystemRelative(rel, input.systemRoot);
    if (uri) block.uri = uri;
    imageBlocks.push(block);
  }
  if (imageBlocks.length === 0) {
    const text4 = fallbackPointers.length > 0 ? appendImageFallbackNote(
      bootstrapText,
      fallbackPointers,
      "no local images could be attached"
    ) : bootstrapText;
    return {
      prompt: [{ type: "text", text: text4 }],
      imagesAttached: false,
      fallbackPointers,
      notes
    };
  }
  let text3 = bootstrapText;
  if (fallbackPointers.length > 0) {
    text3 = appendImageFallbackNote(
      bootstrapText,
      fallbackPointers,
      "some referenced images could not be attached"
    );
  }
  return {
    prompt: [{ type: "text", text: text3 }, ...imageBlocks],
    imagesAttached: true,
    fallbackPointers,
    notes
  };
}
function acpTransportSupportsImage(agentCapabilities) {
  if (!agentCapabilities || typeof agentCapabilities !== "object") return false;
  const caps = agentCapabilities;
  return caps.promptCapabilities?.image === true;
}
function appendImageFallbackNote(bootstrapText, pointers, reason) {
  const unique = [...new Set(pointers.map((p) => p.trim()).filter(Boolean))];
  if (unique.length === 0) return bootstrapText;
  const lines = [
    bootstrapText.trimEnd(),
    "",
    "--- Tent image note ---",
    `Local images kept as Markdown pointers (${reason}).`,
    ...unique.map((p) => `- ${p}`),
    ""
  ];
  return lines.join("\n");
}
function resolveLocalImagePath(rawUrl, fromNotePath) {
  let t = rawUrl.trim().replace(/\\/g, "/");
  if (t.startsWith("<") && t.endsWith(">")) t = t.slice(1, -1).trim();
  if (!t || isExternalOrDataUrl(t)) return null;
  t = (t.split("#")[0]?.split("?")[0] ?? t).trim();
  if (!t) return null;
  if (t === `.tent/${ATTACHMENTS_DIR}` || t.startsWith(`.tent/${ATTACHMENTS_DIR}/`)) {
    t = t.slice(".tent/".length);
  }
  if (t.startsWith("./")) t = t.slice(2);
  if ((t.startsWith("../") || t.includes("/../") || t.startsWith("./")) && fromNotePath) {
    const base = fromNotePath.replace(/\\/g, "/").split("/").slice(0, -1);
    for (const part of t.split("/")) {
      if (part === "." || part === "") continue;
      if (part === "..") {
        if (base.length === 0) return null;
        base.pop();
      } else {
        base.push(part);
      }
    }
    t = base.join("/");
  } else if (fromNotePath && !t.startsWith(ATTACHMENTS_DIR) && !nodePath2.posix.isAbsolute(t)) {
    if (t.startsWith("./")) t = t.slice(2);
    if (!t.startsWith("/") && !t.includes(":")) {
      const dir = fromNotePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
      t = dir ? `${dir}/${t}` : t;
    }
  }
  return normalizeSystemRelativePath(t);
}
function normalizeSystemRelativePath(raw) {
  let t = raw.trim().replace(/\\/g, "/");
  if (!t) return null;
  if (t.startsWith("/") || /^[a-zA-Z]:\//.test(t)) return null;
  if (t.includes("\0")) return null;
  const parts = [];
  for (const p of t.split("/")) {
    if (p === "" || p === ".") continue;
    if (p === "..") {
      if (parts.length === 0) return null;
      parts.pop();
      continue;
    }
    parts.push(p);
  }
  if (parts.length === 0) return null;
  return parts.join("/");
}
function mimeFromPath(relativePath4) {
  const ext = nodePath2.posix.extname(relativePath4).toLowerCase();
  return ACP_IMAGE_MIME_BY_EXT[ext];
}
function isAllowedImageMime(mime) {
  const m = mime.trim().toLowerCase();
  return Object.values(ACP_IMAGE_MIME_BY_EXT).includes(m);
}
function sniffImageMime(bytes) {
  if (bytes.byteLength < 4) return void 0;
  if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
    return "image/png";
  }
  if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
    return "image/jpeg";
  }
  if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56) {
    return "image/gif";
  }
  if (bytes.byteLength >= 12 && bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
    return "image/webp";
  }
  if (bytes[0] === 66 && bytes[1] === 77) {
    return "image/bmp";
  }
  return void 0;
}
function fileUriForSystemRelative(relativePath4, systemRoot) {
  const rel = normalizeSystemRelativePath(relativePath4);
  if (!rel || !systemRoot || !systemRoot.trim()) return void 0;
  const root = nodePath2.resolve(systemRoot.trim());
  const abs = nodePath2.resolve(root, ...rel.split("/"));
  const rootCmp = process.platform === "win32" ? root.toLowerCase() : root;
  const absCmp = process.platform === "win32" ? abs.toLowerCase() : abs;
  if (absCmp !== rootCmp && !absCmp.startsWith(rootCmp + nodePath2.sep)) {
    return void 0;
  }
  return pathToFileURL(abs).href;
}
function mimeExtAgrees(extMime, sniffed) {
  return extMime.trim().toLowerCase() === sniffed.trim().toLowerCase();
}
function isExternalOrDataUrl(href) {
  const t = href.trim();
  if (!t) return true;
  if (t.startsWith("//")) return true;
  if (/^data:/i.test(t)) return true;
  if (/^[a-z][a-z0-9+.-]*:/i.test(t)) {
    return true;
  }
  return false;
}
function walkMdast(node2, visit) {
  visit(node2);
  if ("children" in node2 && Array.isArray(node2.children)) {
    for (const child of node2.children) walkMdast(child, visit);
  }
}
async function collectBootstrapImageRefsFromTask(input) {
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  const pushAll = (refs) => {
    for (const r of refs) {
      const key2 = r.relativePath.toLowerCase();
      if (seen.has(key2)) continue;
      seen.add(key2);
      out.push(r);
    }
  };
  pushAll(extractMarkdownImageRefs(input.prompt || ""));
  for (const claim of input.claimBodies ?? []) {
    pushAll(
      extractMarkdownImageRefs(claim.body || "", {
        fromNotePath: claim.notePath
      })
    );
  }
  return out.slice(0, MAX_ACP_IMAGES_PER_PROMPT * 2);
}

// src/adapters/acp/limits.ts
import { StringDecoder } from "node:string_decoder";
var ACP_OUTPUT_LIMIT_CODE = "ACP_OUTPUT_LIMIT";
var ACP_REQUEST_LIMIT_CODE = "ACP_REQUEST_LIMIT";
var DEFAULT_ACP_ASSISTANT_REPORT_BYTES = 4 * 1024 * 1024;
var DEFAULT_ACP_ASSISTANT_SEGMENTS = 4096;
var DEFAULT_ACP_NO_PROGRESS_UPDATES = 65536;
var DEFAULT_ACP_DIAGNOSTIC_EVENTS = 256;
var DEFAULT_ACP_STDOUT_FRAME_BYTES = 8 * 1024 * 1024;
var DEFAULT_ACP_BOOTSTRAP_TEXT_BYTES = 4 * 1024 * 1024;
var DEFAULT_ACP_REQUEST_FRAME_BYTES = 40 * 1024 * 1024;
var ACP_DIAGNOSTIC_EVENT_BYTES = 16 * 1024;
var DEFAULT_ACP_RESOURCE_LIMITS = {
  assistantReportBytes: DEFAULT_ACP_ASSISTANT_REPORT_BYTES,
  assistantSegments: DEFAULT_ACP_ASSISTANT_SEGMENTS,
  noProgressUpdates: DEFAULT_ACP_NO_PROGRESS_UPDATES,
  diagnosticEvents: DEFAULT_ACP_DIAGNOSTIC_EVENTS,
  stdoutFrameBytes: DEFAULT_ACP_STDOUT_FRAME_BYTES,
  bootstrapTextBytes: DEFAULT_ACP_BOOTSTRAP_TEXT_BYTES,
  requestFrameBytes: DEFAULT_ACP_REQUEST_FRAME_BYTES
};
var AcpLimitError = class extends Error {
  constructor(code, detail) {
    super(`${code}: ${detail}`);
    this.name = "AcpLimitError";
    this.code = code;
  }
};
function isAcpLimitError(value) {
  if (!(value instanceof Error)) return false;
  const code = value.code;
  return code === ACP_OUTPUT_LIMIT_CODE || code === ACP_REQUEST_LIMIT_CODE;
}
function resolveAcpResourceLimits(overrides) {
  const resolved = { ...DEFAULT_ACP_RESOURCE_LIMITS, ...overrides ?? {} };
  for (const [name, value] of Object.entries(resolved)) {
    if (!Number.isSafeInteger(value) || value <= 0) {
      throw new Error(`Invalid ACP resource limit ${name}=${value}`);
    }
  }
  return resolved;
}
function utf8Bytes(text3) {
  return Buffer.byteLength(text3, "utf8");
}
var TRUNCATED_MARKER = "\n\u2026[truncated]";
var REDACTED_MARKER = "[redacted]";
var MAX_DIAGNOSTIC_SECRET_LOOKAHEAD_BYTES = 4096;
function truncateUtf8Text(text3, maxBytes = ACP_DIAGNOSTIC_EVENT_BYTES) {
  if (maxBytes <= 0) return "";
  if (utf8Bytes(text3) <= maxBytes) return text3;
  const markerBytes = utf8Bytes(TRUNCATED_MARKER);
  if (markerBytes >= maxBytes) {
    const marker = Buffer.allocUnsafe(maxBytes);
    const markerWritten = marker.write(TRUNCATED_MARKER, 0, maxBytes, "utf8");
    return marker.subarray(0, markerWritten).toString("utf8");
  }
  const payloadBytes = Math.max(0, maxBytes - markerBytes);
  const buffer = Buffer.allocUnsafe(payloadBytes);
  const written = buffer.write(text3, 0, payloadBytes, "utf8");
  return buffer.subarray(0, written).toString("utf8") + TRUNCATED_MARKER;
}
function appendUtf8Tail(current, next, maxBytes) {
  const combined = current + next;
  if (utf8Bytes(combined) <= maxBytes) return combined;
  const encoded = Buffer.from(combined, "utf8");
  return truncateUtf8Text(
    encoded.subarray(Math.max(0, encoded.byteLength - maxBytes)).toString("utf8"),
    maxBytes
  );
}
function utf8Prefix(text3, maxBytes) {
  if (utf8Bytes(text3) <= maxBytes) return { text: text3, truncated: false };
  const buffer = Buffer.allocUnsafe(maxBytes);
  const written = buffer.write(text3, 0, maxBytes, "utf8");
  return {
    text: buffer.subarray(0, written).toString("utf8"),
    truncated: true
  };
}
function activeDiagnosticSecrets(secrets) {
  return [...new Set(secrets.filter((secret) => secret.length >= 4))].sort(
    (a, b) => b.length - a.length
  );
}
function redactDiagnosticSecrets(raw, secrets) {
  const intervals = [];
  for (const secret of secrets) {
    let found = raw.indexOf(secret);
    while (found >= 0) {
      intervals.push({ start: found, end: found + secret.length });
      found = raw.indexOf(secret, found + 1);
    }
  }
  if (intervals.length === 0) return raw;
  intervals.sort((left, right) => left.start - right.start || left.end - right.end);
  let output = "";
  let cursor = 0;
  let start = intervals[0].start;
  let end = intervals[0].end;
  for (const interval of intervals.slice(1)) {
    if (interval.start <= end) {
      end = Math.max(end, interval.end);
      continue;
    }
    output += raw.slice(cursor, start) + REDACTED_MARKER;
    cursor = end;
    start = interval.start;
    end = interval.end;
  }
  return output + raw.slice(cursor, start) + REDACTED_MARKER + raw.slice(end);
}
function diagnosticCarryChars(raw, secrets) {
  let lastCompleteSecretEnd = 0;
  for (const secret of secrets) {
    let found = raw.indexOf(secret);
    while (found >= 0) {
      lastCompleteSecretEnd = Math.max(
        lastCompleteSecretEnd,
        found + secret.length
      );
      found = raw.indexOf(secret, found + 1);
    }
  }
  let carryChars = 0;
  for (const secret of secrets) {
    const max = Math.min(secret.length - 1, raw.length);
    for (let length = max; length > carryChars; length -= 1) {
      const start = raw.length - length;
      if (start >= lastCompleteSecretEnd && raw.endsWith(secret.slice(0, length))) {
        carryChars = length;
        break;
      }
    }
  }
  return carryChars;
}
function redactFinalDiagnosticTail(raw, secrets) {
  let partialChars = 0;
  for (const secret of secrets) {
    const max = Math.min(secret.length - 1, raw.length);
    for (let length = max; length >= 1; length -= 1) {
      if (raw.endsWith(secret.slice(0, length))) {
        partialChars = Math.max(partialChars, length);
        break;
      }
    }
  }
  if (partialChars === 0) return redactDiagnosticSecrets(raw, secrets);
  return redactDiagnosticSecrets(
    raw.slice(0, raw.length - partialChars),
    secrets
  ) + REDACTED_MARKER;
}
var BoundedDiagnosticRedactor = class {
  constructor(secrets, maxBytes = ACP_DIAGNOSTIC_EVENT_BYTES) {
    this.maxBytes = maxBytes;
    this.carry = "";
    this.bufferDecoder = new StringDecoder("utf8");
    this.discardUntilFlush = false;
    this.secrets = activeDiagnosticSecrets(secrets);
    const oversizedSecret = this.secrets.find(
      (secret) => utf8Bytes(secret) > MAX_DIAGNOSTIC_SECRET_LOOKAHEAD_BYTES
    );
    if (oversizedSecret) {
      this.maxSecretChars = -1;
      this.lookaheadBytes = 0;
      return;
    }
    this.maxSecretChars = Math.max(
      1,
      ...this.secrets.map((secret) => secret.length)
    );
    this.lookaheadBytes = Math.max(
      0,
      ...this.secrets.map((secret) => utf8Bytes(secret))
    );
  }
  pushText(text3) {
    if (this.discardUntilFlush) return "";
    return this.project(utf8Prefix(text3, this.inputWindowBytes()));
  }
  pushBuffer(value) {
    if (this.discardUntilFlush) return "";
    const windowBytes = this.inputWindowBytes();
    const truncated = value.byteLength > windowBytes;
    let text3 = this.bufferDecoder.write(
      truncated ? value.subarray(0, windowBytes) : value
    );
    if (truncated) {
      text3 += this.bufferDecoder.end();
      this.bufferDecoder = new StringDecoder("utf8");
    }
    return this.project({ text: text3, truncated });
  }
  flush() {
    if (this.discardUntilFlush) {
      this.bufferDecoder.end();
      this.bufferDecoder = new StringDecoder("utf8");
      this.carry = "";
      this.discardUntilFlush = false;
      return "";
    }
    const decodedTail = this.bufferDecoder.end();
    this.bufferDecoder = new StringDecoder("utf8");
    const decodedHead = decodedTail ? this.project({ text: decodedTail, truncated: false }) : "";
    if (!this.carry) return truncateUtf8Text(decodedHead, this.maxBytes);
    if (this.maxSecretChars < 0) {
      this.carry = "";
      return joinBoundedDiagnosticParts(
        decodedHead,
        REDACTED_MARKER,
        this.maxBytes
      );
    }
    const raw = this.carry;
    this.carry = "";
    return joinBoundedDiagnosticParts(
      decodedHead,
      redactFinalDiagnosticTail(raw, this.secrets),
      this.maxBytes
    );
  }
  inputWindowBytes() {
    return this.maxBytes + this.lookaheadBytes * 2;
  }
  project(input) {
    if (input.truncated) this.discardUntilFlush = true;
    if (this.maxSecretChars < 0) {
      this.carry = "";
      return truncateUtf8Text(REDACTED_MARKER, this.maxBytes);
    }
    const raw = this.carry + input.text;
    this.carry = "";
    let safeRaw;
    if (input.truncated) {
      safeRaw = raw;
    } else {
      const retainedChars = diagnosticCarryChars(raw, this.secrets);
      const boundary = raw.length - retainedChars;
      safeRaw = raw.slice(0, boundary);
      this.carry = raw.slice(boundary);
    }
    let redacted = input.truncated ? redactFinalDiagnosticTail(safeRaw, this.secrets) : redactDiagnosticSecrets(safeRaw, this.secrets);
    if (redacted !== safeRaw) {
      redacted = `${REDACTED_MARKER}
${redacted}`;
    }
    if (input.truncated) redacted += TRUNCATED_MARKER;
    return truncateUtf8Text(redacted, this.maxBytes);
  }
};
function joinBoundedDiagnosticParts(head, tail, maxBytes) {
  if (!tail) return truncateUtf8Text(head, maxBytes);
  const boundedTail = truncateUtf8Text(tail, maxBytes);
  const headBudget = Math.max(0, maxBytes - utf8Bytes(boundedTail));
  return truncateUtf8Text(head, headBudget) + boundedTail;
}
function redactBoundedDiagnosticText(text3, secrets, maxBytes = ACP_DIAGNOSTIC_EVENT_BYTES) {
  const redactor = new BoundedDiagnosticRedactor(secrets, maxBytes);
  const head = redactor.pushText(text3);
  const tail = redactor.flush();
  return joinBoundedDiagnosticParts(head, tail, maxBytes);
}

// src/adapters/acp/types.ts
var DEFAULT_PROMPT_TIMEOUT_MS = 30 * 6e4;
var DEFAULT_PERMISSION_TIMEOUT_MS = 12e4;
var ACP_SESSION_AUTH_METHODS_MAX = 64;
var ACP_SESSION_CONFIG_OPTIONS_MAX = 128;
var ACP_SESSION_CONFIG_VALUES_MAX = 256;
var ACP_SESSION_CONFIG_GROUPS_MAX = 64;
var ACP_SESSION_CONFIG_SNAPSHOT_BYTES = 256 * 1024;
var ACP_SESSION_CONFIG_ID_BYTES = 256;
var ACP_SESSION_CONFIG_LABEL_BYTES = 1024;
var ACP_SESSION_CONFIG_DESCRIPTION_BYTES = 4096;
function plainRecord2(value) {
  return value !== null && typeof value === "object" && !Array.isArray(value);
}
function boundedString(value, maxBytes, allowEmpty = false, scrubText) {
  if (typeof value !== "string") return void 0;
  const scrubbed = scrubText ? scrubText(value) : value;
  if (scrubbed === void 0) return void 0;
  if (!allowEmpty && scrubbed.length === 0) return void 0;
  return utf8Bytes(scrubbed) <= maxBytes ? scrubbed : void 0;
}
function boundedDisplayString(value, maxBytes, bounds, policy, allowEmpty = false) {
  const containedSecret = typeof value === "string" && policy.containsSecret?.(value) === true;
  const normalized = boundedString(
    value,
    maxBytes,
    allowEmpty,
    policy.scrubDisplayText
  );
  if (containedSecret && normalized === void 0) bounds.truncated = true;
  return normalized;
}
function optionalBoundedDisplayString(value, maxBytes, bounds, policy) {
  if (value === void 0) return void 0;
  return boundedDisplayString(value, maxBytes, bounds, policy, true) ?? null;
}
function normalizeConfigOptionValue(value, bounds, policy) {
  if (!plainRecord2(value)) return void 0;
  if (typeof value.value === "string" && policy.containsSecret?.(value.value)) {
    bounds.truncated = true;
    return void 0;
  }
  const id = boundedString(value.value, ACP_SESSION_CONFIG_ID_BYTES);
  const name = boundedDisplayString(
    value.name,
    ACP_SESSION_CONFIG_LABEL_BYTES,
    bounds,
    policy
  );
  const description = optionalBoundedDisplayString(
    value.description,
    ACP_SESSION_CONFIG_DESCRIPTION_BYTES,
    bounds,
    policy
  );
  if (!id || !name || description === null) return void 0;
  return {
    value: id,
    name,
    ...description !== void 0 ? { description } : {}
  };
}
function normalizeConfigOption(value, bounds, policy) {
  if (!plainRecord2(value)) return void 0;
  if (value.type !== "boolean" && value.type !== "select") return void 0;
  if (typeof value.id === "string" && policy.containsSecret?.(value.id)) {
    bounds.truncated = true;
    return void 0;
  }
  const id = boundedString(value.id, ACP_SESSION_CONFIG_ID_BYTES);
  const name = boundedDisplayString(
    value.name,
    ACP_SESSION_CONFIG_LABEL_BYTES,
    bounds,
    policy
  );
  const description = optionalBoundedDisplayString(
    value.description,
    ACP_SESSION_CONFIG_DESCRIPTION_BYTES,
    bounds,
    policy
  );
  const category = optionalBoundedDisplayString(
    value.category,
    ACP_SESSION_CONFIG_ID_BYTES,
    bounds,
    policy
  );
  if (!id || !name || description === null || category === null) return void 0;
  const common = {
    id,
    name,
    ...description !== void 0 ? { description } : {},
    ...category !== void 0 ? { category } : {}
  };
  if (value.type === "boolean") {
    if (typeof value.currentValue !== "boolean") return void 0;
    return { ...common, type: "boolean", currentValue: value.currentValue };
  }
  if (typeof value.currentValue === "string" && policy.containsSecret?.(value.currentValue)) {
    bounds.truncated = true;
    return void 0;
  }
  const currentValue = boundedString(value.currentValue, ACP_SESSION_CONFIG_ID_BYTES);
  if (!currentValue) return void 0;
  if (!Array.isArray(value.options)) return void 0;
  const grouped = value.options.some(
    (option) => plainRecord2(option) && "group" in option
  );
  let options;
  if (grouped) {
    const groups = [];
    const seenGroups = /* @__PURE__ */ new Set();
    const seenValues = /* @__PURE__ */ new Set();
    let valueCount = 0;
    if (value.options.length > ACP_SESSION_CONFIG_GROUPS_MAX) bounds.truncated = true;
    for (const rawGroup of value.options.slice(0, ACP_SESSION_CONFIG_GROUPS_MAX)) {
      if (!plainRecord2(rawGroup) || !Array.isArray(rawGroup.options)) continue;
      if (typeof rawGroup.group === "string" && policy.containsSecret?.(rawGroup.group)) {
        bounds.truncated = true;
        continue;
      }
      const group = boundedString(
        rawGroup.group,
        ACP_SESSION_CONFIG_ID_BYTES
      );
      const groupName = boundedDisplayString(
        rawGroup.name,
        ACP_SESSION_CONFIG_LABEL_BYTES,
        bounds,
        policy
      );
      if (!group || !groupName || seenGroups.has(group)) continue;
      const groupOptions = [];
      for (const raw of rawGroup.options) {
        if (valueCount >= ACP_SESSION_CONFIG_VALUES_MAX) {
          bounds.truncated = true;
          break;
        }
        valueCount += 1;
        const option = normalizeConfigOptionValue(raw, bounds, policy);
        if (!option || seenValues.has(option.value)) continue;
        seenValues.add(option.value);
        groupOptions.push(option);
      }
      if (groupOptions.length === 0) continue;
      seenGroups.add(group);
      groups.push({ group, name: groupName, options: groupOptions });
      if (valueCount >= ACP_SESSION_CONFIG_VALUES_MAX) break;
    }
    if (groups.length === 0) return void 0;
    options = { kind: "grouped", groups };
  } else {
    const flatOptions = [];
    const seen = /* @__PURE__ */ new Set();
    if (value.options.length > ACP_SESSION_CONFIG_VALUES_MAX) bounds.truncated = true;
    for (const raw of value.options.slice(0, ACP_SESSION_CONFIG_VALUES_MAX)) {
      const option = normalizeConfigOptionValue(raw, bounds, policy);
      if (!option || seen.has(option.value)) continue;
      seen.add(option.value);
      flatOptions.push(option);
    }
    if (flatOptions.length === 0) return void 0;
    options = { kind: "flat", options: flatOptions };
  }
  const survivingValues = options.kind === "flat" ? options.options.map((option) => option.value) : options.groups.flatMap(
    (group) => group.options.map((option) => option.value)
  );
  if (!survivingValues.includes(currentValue)) {
    bounds.truncated = true;
    return void 0;
  }
  return {
    ...common,
    type: "select",
    currentValue,
    options
  };
}
function normalizeAuthMethodIds(value, policy) {
  if (!Array.isArray(value)) return { ids: [], truncated: false };
  const ids = [];
  const seen = /* @__PURE__ */ new Set();
  let truncated = value.length > ACP_SESSION_AUTH_METHODS_MAX;
  for (const raw of value.slice(0, ACP_SESSION_AUTH_METHODS_MAX)) {
    if (plainRecord2(raw) && typeof raw.id === "string" && policy.containsSecret?.(raw.id)) {
      truncated = true;
      continue;
    }
    const id = plainRecord2(raw) ? boundedString(raw.id, ACP_SESSION_CONFIG_ID_BYTES) : void 0;
    if (!id || seen.has(id)) continue;
    seen.add(id);
    ids.push(id);
  }
  return { ids, truncated };
}
function createAcpSessionConfigSnapshot(input) {
  const capabilities = plainRecord2(input.agentCapabilities) ? input.agentCapabilities : {};
  const sessionCapabilities = plainRecord2(capabilities.sessionCapabilities) ? capabilities.sessionCapabilities : {};
  const promptCapabilities = plainRecord2(capabilities.promptCapabilities) ? capabilities.promptCapabilities : {};
  const policy = {
    scrubDisplayText: input.scrubDisplayText,
    containsSecret: input.containsSecret
  };
  const auth = normalizeAuthMethodIds(input.authMethods, policy);
  const rawOptions = Array.isArray(input.configOptions) ? input.configOptions : [];
  const configOptions = [];
  const seen = /* @__PURE__ */ new Set();
  const bounds = { truncated: false };
  let bytes = 2;
  let truncated = auth.truncated || rawOptions.length > ACP_SESSION_CONFIG_OPTIONS_MAX;
  for (const raw of rawOptions.slice(0, ACP_SESSION_CONFIG_OPTIONS_MAX)) {
    const option = normalizeConfigOption(raw, bounds, policy);
    if (!option || seen.has(option.id)) continue;
    const optionBytes = utf8Bytes(JSON.stringify(option)) + 1;
    if (bytes + optionBytes > ACP_SESSION_CONFIG_SNAPSHOT_BYTES) {
      truncated = true;
      break;
    }
    bytes += optionBytes;
    seen.add(option.id);
    configOptions.push(option);
  }
  return {
    capabilities: {
      loadSession: capabilities.loadSession === true,
      resumeSession: plainRecord2(sessionCapabilities.resume),
      promptImage: promptCapabilities.image === true
    },
    authMethodIds: auth.ids,
    configOptions,
    truncated: truncated || bounds.truncated
  };
}
function cloneAcpSessionConfigSnapshot(snapshot) {
  return {
    capabilities: { ...snapshot.capabilities },
    authMethodIds: [...snapshot.authMethodIds],
    configOptions: snapshot.configOptions.map(
      (option) => option.type === "select" ? {
        ...option,
        options: option.options.kind === "flat" ? {
          kind: "flat",
          options: option.options.options.map((value) => ({ ...value }))
        } : {
          kind: "grouped",
          groups: option.options.groups.map((group) => ({
            ...group,
            options: group.options.map((value) => ({ ...value }))
          }))
        }
      } : { ...option }
    ),
    truncated: snapshot.truncated
  };
}
function parseAcpSessionConfigSnapshot(value) {
  if (!plainRecord2(value)) return null;
  if (Object.keys(value).some(
    (key2) => key2 !== "capabilities" && key2 !== "authMethodIds" && key2 !== "configOptions" && key2 !== "truncated"
  )) {
    return null;
  }
  if (!plainRecord2(value.capabilities)) return null;
  if (Object.keys(value.capabilities).some(
    (key2) => key2 !== "loadSession" && key2 !== "resumeSession" && key2 !== "promptImage"
  ) || typeof value.capabilities.loadSession !== "boolean" || typeof value.capabilities.resumeSession !== "boolean" || typeof value.capabilities.promptImage !== "boolean") {
    return null;
  }
  if (!Array.isArray(value.authMethodIds) || !Array.isArray(value.configOptions)) {
    return null;
  }
  if (typeof value.truncated !== "boolean") return null;
  const wireOptions = value.configOptions.map((option) => {
    if (!plainRecord2(option) || option.type !== "select") return option;
    if (!plainRecord2(option.options)) return option;
    if (option.options.kind === "flat" && Array.isArray(option.options.options)) {
      return { ...option, options: option.options.options };
    }
    if (option.options.kind === "grouped" && Array.isArray(option.options.groups)) {
      return { ...option, options: option.options.groups };
    }
    return option;
  });
  const normalized = createAcpSessionConfigSnapshot({
    agentCapabilities: {
      loadSession: value.capabilities.loadSession,
      sessionCapabilities: value.capabilities.resumeSession ? { resume: {} } : {},
      promptCapabilities: { image: value.capabilities.promptImage }
    },
    authMethods: value.authMethodIds.map((id) => ({ id })),
    configOptions: wireOptions
  });
  if (normalized.truncated || JSON.stringify(normalized.authMethodIds) !== JSON.stringify(value.authMethodIds) || JSON.stringify(normalized.configOptions) !== JSON.stringify(value.configOptions)) {
    return null;
  }
  normalized.truncated = value.truncated;
  return normalized;
}

// src/core/output.ts
var OUTPUT_PROVENANCE_FIELD = "resultId";
var OutputProvenanceError = class extends Error {
  constructor(code, message2, details) {
    super(message2);
    this.code = code;
    this.name = "OutputProvenanceError";
    this.details = details;
  }
};
function isOutputNodeType(type) {
  return typeof type === "string" && type.trim() === "output";
}
function readOutputTaskResultId(fm) {
  const raw = fm[OUTPUT_PROVENANCE_FIELD];
  if (typeof raw !== "string") return void 0;
  const trimmed = raw.trim();
  return trimmed || void 0;
}
function collectReferencedTaskResultIds(tent) {
  const out = /* @__PURE__ */ new Set();
  for (const node2 of tent.byId.values()) {
    const resultId = readOutputTaskResultId(node2.fm);
    if (resultId && isTaskResultId(resultId)) out.add(resultId);
  }
  return out;
}
function assertOutputBindable(node2, resultId) {
  if (!node2.id) {
    throw new OutputProvenanceError("OUTPUT_INVALID", `Output has no id: ${node2.path}`);
  }
  if (node2.invalid) {
    throw new OutputProvenanceError(
      "OUTPUT_INVALID",
      `Output is invalid: ${node2.path}`,
      { outputId: node2.id, detail: node2.invalidReason }
    );
  }
  if (node2.archived || node2.mode === "archived") {
    throw new OutputProvenanceError(
      "OUTPUT_ARCHIVED",
      `Output is archived and cannot bind provenance: ${node2.id}`,
      { outputId: node2.id }
    );
  }
  if (!isOutputNodeType(node2.type)) {
    throw new OutputProvenanceError(
      "OUTPUT_NOT_OUTPUT_TYPE",
      `Node type must be output to bind provenance (got ${node2.type}): ${node2.id}`,
      { outputId: node2.id, type: node2.type }
    );
  }
  if (!isTaskResultId(resultId)) {
    throw new OutputProvenanceError(
      "INVALID_RESULT_ID",
      `Invalid result id for provenance bind: ${resultId}`
    );
  }
  const existing = readOutputTaskResultId(node2.fm);
  if (existing && existing !== resultId) {
    throw new OutputProvenanceError(
      "OUTPUT_ALREADY_BOUND",
      `Output ${node2.id} is already bound to ${existing}; cannot rebind to ${resultId}`,
      { outputId: node2.id, existingTaskResultId: existing, resultId }
    );
  }
  return { alreadyBound: existing === resultId };
}
function validateOutputResultBinding(tent, outputNodeIds, resultId) {
  if (!outputNodeIds || outputNodeIds.length === 0) {
    return { outputIds: [], nodes: [] };
  }
  if (!isTaskResultId(resultId)) {
    throw new OutputProvenanceError(
      "INVALID_RESULT_ID",
      `Invalid result id for provenance bind: ${resultId}`
    );
  }
  const seen = /* @__PURE__ */ new Set();
  const outputIds = [];
  const nodes = [];
  for (const raw of outputNodeIds) {
    if (typeof raw !== "string" || !raw.trim()) {
      throw new OutputProvenanceError(
        "INVALID_SELECTOR",
        "outputNodeIds entries must be non-empty Node ids"
      );
    }
    const id = raw.trim();
    if (seen.has(id)) continue;
    seen.add(id);
    if (tent.duplicateIds.has(id)) {
      throw new OutputProvenanceError(
        "OUTPUT_INVALID",
        `Duplicate node id '${id}' found; repair before binding provenance.`,
        { outputId: id }
      );
    }
    const node2 = tent.byId.get(id);
    if (!node2) {
      throw new OutputProvenanceError("OUTPUT_NOT_FOUND", `Output Node not found: ${id}`, {
        outputId: id
      });
    }
    assertOutputBindable(node2, resultId);
    outputIds.push(id);
    nodes.push(node2);
  }
  return { outputIds, nodes };
}
async function restoreOutputBindSnapshots(fs21, snapshots) {
  if (snapshots.length === 0) return;
  const failures = [];
  for (let i = snapshots.length - 1; i >= 0; i--) {
    const snap = snapshots[i];
    try {
      await fs21.writeFile(snap.notePath, snap.raw);
      const prev = snap.previousTaskResultId;
      if (prev === void 0) {
        delete snap.node.fm[OUTPUT_PROVENANCE_FIELD];
      } else {
        snap.node.fm[OUTPUT_PROVENANCE_FIELD] = prev;
      }
    } catch (err) {
      failures.push({
        outputId: snap.outputId,
        notePath: snap.notePath,
        error: err instanceof Error ? err.message : String(err)
      });
    }
  }
  if (failures.length > 0) {
    throw new OutputProvenanceError(
      "BIND_ROLLBACK_FAILED",
      `Failed to roll back Output provenance bind for ${failures.length} file(s); disk may be partially bound.`,
      { failures }
    );
  }
}
async function bindOutputsToTaskResultUnlocked(fs21, tent, outputNodeIds, resultId) {
  const { outputIds, nodes } = validateOutputResultBinding(tent, outputNodeIds, resultId);
  const planned = [];
  for (let i = 0; i < nodes.length; i++) {
    const node2 = nodes[i];
    const outputId = outputIds[i];
    const { alreadyBound } = assertOutputBindable(node2, resultId);
    if (alreadyBound) continue;
    const notePath = nodeNotePath(node2.path);
    const raw = await fs21.readFile(notePath);
    planned.push({
      node: node2,
      outputId,
      notePath,
      raw,
      previousTaskResultId: readOutputTaskResultId(node2.fm)
    });
  }
  const snapshots = [];
  const changedIds = [];
  try {
    for (const item of planned) {
      const { data, body, keyOrder } = parseFrontmatter(item.raw);
      data[OUTPUT_PROVENANCE_FIELD] = resultId;
      const nextRaw = serializeFrontmatter(data, body, outputKeyOrder(keyOrder));
      await fs21.writeFile(item.notePath, nextRaw);
      snapshots.push({
        outputId: item.outputId,
        notePath: item.notePath,
        raw: item.raw,
        node: item.node,
        previousTaskResultId: item.previousTaskResultId
      });
      item.node.fm[OUTPUT_PROVENANCE_FIELD] = resultId;
      changedIds.push(item.outputId);
    }
  } catch (err) {
    try {
      await restoreOutputBindSnapshots(fs21, snapshots);
    } catch (rollbackErr) {
      if (rollbackErr instanceof OutputProvenanceError) throw rollbackErr;
      throw new OutputProvenanceError(
        "BIND_ROLLBACK_FAILED",
        `Output provenance bind failed and rollback also failed: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)} (original: ${err instanceof Error ? err.message : String(err)})`,
        {
          originalError: err instanceof Error ? err.message : String(err)
        }
      );
    }
    throw err;
  }
  return { boundIds: outputIds, changedIds, snapshots };
}
async function bindOutputsToTaskResult(fs21, outputNodeIds, resultId) {
  return withTentMutation(fs21, async () => {
    const tent = await loadTent(fs21);
    const bound = await bindOutputsToTaskResultUnlocked(
      fs21,
      tent,
      outputNodeIds,
      resultId
    );
    return { boundIds: bound.boundIds, changedIds: bound.changedIds };
  });
}
function projectOutputProvenance(node2, indexes) {
  if (node2.invalid) {
    throw new OutputProvenanceError(
      "OUTPUT_INVALID",
      `Output is invalid: ${node2.path}`,
      { outputId: node2.id, detail: node2.invalidReason }
    );
  }
  if (!isOutputNodeType(node2.type)) {
    throw new OutputProvenanceError(
      "OUTPUT_NOT_OUTPUT_TYPE",
      `Node type must be output for provenance query (got ${node2.type}): ${node2.id}`,
      { outputId: node2.id, type: node2.type }
    );
  }
  const resultId = readOutputTaskResultId(node2.fm) ?? null;
  const base = {
    outputId: node2.id,
    path: node2.path,
    bound: false,
    resultId: null,
    result: null,
    task: null,
    incomplete: []
  };
  if (!resultId) {
    return base;
  }
  base.bound = true;
  base.resultId = resultId;
  if (!isTaskResultId(resultId)) {
    base.incomplete.push("result_missing");
    return base;
  }
  const result = indexes.resultsById.get(resultId);
  if (!result) {
    base.incomplete.push("result_missing");
    return base;
  }
  base.result = {
    id: result.id,
    status: result.status,
    taskId: result.taskId,
    artifactRefs: result.artifactRefs.map((ref) => ({ ...ref }))
  };
  const task = indexes.tasksById.get(result.taskId);
  if (!task) {
    base.incomplete.push("task_missing");
  } else {
    base.task = {
      id: task.id || task.path,
      state: task.state,
      path: task.path,
      nodeIds: [...task.nodeIds]
    };
    const taskKey = task.id || task.path;
    if (result.taskId && taskKey && result.taskId !== taskKey && result.taskId !== task.path) {
      if (!base.incomplete.includes("mismatch")) base.incomplete.push("mismatch");
    }
  }
  return base;
}
async function loadProvenanceIndexes(fs21, tent) {
  const loadedTent = tent ?? await loadTent(fs21);
  const results = await loadTaskResults(fs21);
  const tasks = await loadTaskRecords(fs21);
  const resultsById = /* @__PURE__ */ new Map();
  for (const result of results) resultsById.set(result.id, result);
  const tasksById = /* @__PURE__ */ new Map();
  for (const t of tasks) {
    if (t.id) tasksById.set(t.id, t);
  }
  return { tent: loadedTent, resultsById, tasksById };
}
async function resolveOutputProvenance(fs21, selector, preloaded) {
  const indexes = preloaded ?? await loadProvenanceIndexes(fs21);
  const node2 = resolveOutputNode(indexes.tent, selector);
  return projectOutputProvenance(node2, indexes);
}
function resolveOutputNode(tent, selector) {
  const nodeId = selector.nodeId.trim();
  if (!nodeId) {
    throw new OutputProvenanceError("INVALID_SELECTOR", "output.provenance requires nodeId");
  }
  if (tent.duplicateIds.has(nodeId)) {
    throw new OutputProvenanceError(
      "OUTPUT_INVALID",
      `Duplicate Node id '${nodeId}' found; repair before provenance query.`,
      { nodeId }
    );
  }
  const node2 = tent.byId.get(nodeId);
  if (!node2) {
    throw new OutputProvenanceError("OUTPUT_NOT_FOUND", `Output Node not found: ${nodeId}`, {
      nodeId
    });
  }
  return node2;
}
function outputKeyOrder(existing) {
  const preferred = [...NODE_FRONTMATTER_KEY_ORDER, OUTPUT_PROVENANCE_FIELD];
  return [
    ...preferred,
    ...existing.filter((key2) => !preferred.includes(key2))
  ];
}

// src/core/proposal.ts
async function submitProposal(fs21, clock, role, nodeId, body) {
  return withTentMutation(fs21, async () => submitProposalUnlocked(fs21, clock, role, nodeId, body));
}
async function submitProposalUnlocked(fs21, clock, roleInput, nodeId, body) {
  const text3 = body.trim();
  if (!text3) throw new Error("Proposal body cannot be empty.");
  const role = normalizeRole(roleInput);
  const tent = await loadTent(fs21);
  if (tent.duplicateIds.has(nodeId)) {
    throw new Error(`Duplicate Node id '${nodeId}' found; repair the duplicate Nodes before using this id.`);
  }
  const node2 = tent.byId.get(nodeId);
  if (!node2) throw new Error(`Node not found: ${nodeId}.`);
  const path23 = proposalPath(role, node2.id);
  if (await fs21.exists(path23)) {
    const current = await loadProposal(fs21, path23);
    if (current.status === "pending") throw new Error("A proposal is already pending triage; the user must confirm or reject it first.");
  }
  const proposal = {
    path: path23,
    nodeId: node2.id,
    role,
    status: "pending",
    createdAt: clock.now(),
    body: text3
  };
  await ensureDir4(fs21, join("temp", role, "proposals"));
  await writeProposal(fs21, proposal);
  return proposal;
}
async function loadProposals(fs21) {
  const proposals = [];
  if (!await fs21.exists("temp")) return proposals;
  for (const roleDir of await fs21.listDir("temp")) {
    if (!roleDir.isDir) continue;
    const dir = join("temp", roleDir.name, "proposals");
    if (!await fs21.exists(dir)) continue;
    for (const entry of await fs21.listDir(dir)) {
      if (entry.isDir || !entry.name.endsWith(".md")) continue;
      const path23 = join(dir, entry.name);
      try {
        proposals.push(await loadProposal(fs21, path23));
      } catch {
      }
    }
  }
  return proposals.sort((a, b) => (b.createdAt || "").localeCompare(a.createdAt || ""));
}
async function loadProposal(fs21, inputPath) {
  const path23 = normalizeProposalPath(inputPath);
  if (!await fs21.exists(path23)) throw new Error(`Proposal not found: ${path23}.`);
  const { data, body } = parseFrontmatter(await fs21.readFile(path23));
  if (data.type !== "proposal" || typeof data.nodeId !== "string" || !isNodeId(data.nodeId) || typeof data.role !== "string" || data.status !== "pending" && data.status !== "accepted" && data.status !== "rejected") {
    throw new Error(`Invalid proposal format: ${path23}.`);
  }
  return {
    path: path23,
    nodeId: data.nodeId,
    role: data.role,
    status: data.status,
    createdAt: typeof data.createdAt === "string" ? data.createdAt : void 0,
    body: body.trim()
  };
}
async function acceptProposal(fs21, inputPath) {
  await withTentMutation(fs21, async () => {
    const proposal = await loadProposal(fs21, inputPath);
    if (proposal.status !== "pending") throw new Error("Only pending proposals can be accepted.");
    proposal.status = "accepted";
    await writeProposal(fs21, proposal);
  });
}
async function rejectProposal(fs21, inputPath) {
  await withTentMutation(fs21, async () => {
    const proposal = await loadProposal(fs21, inputPath);
    if (proposal.status !== "pending") throw new Error("Only pending proposals can be rejected.");
    proposal.status = "rejected";
    await writeProposal(fs21, proposal);
  });
}
function proposalPath(role, nodeId) {
  return join("temp", role, "proposals", `${nodeId}.md`);
}
function normalizeProposalPath(input) {
  const path23 = input.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
  const match = /^temp\/[^/]+\/proposals\/([^/]+)\.md$/.exec(path23);
  if (!match || !isNodeId(match[1])) {
    throw new Error("Proposal must point to temp/<role>/proposals/<nodeId>.md.");
  }
  return path23;
}
async function writeProposal(fs21, proposal) {
  const data = {
    type: "proposal",
    nodeId: proposal.nodeId,
    role: proposal.role,
    status: proposal.status,
    createdAt: proposal.createdAt
  };
  await fs21.writeFile(
    proposal.path,
    serializeFrontmatter(data, proposal.body + "\n", ["type", "nodeId", "role", "status", "createdAt"])
  );
}
async function ensureDir4(fs21, path23) {
  if (!await fs21.exists(path23)) await fs21.mkdir(path23);
}
function normalizeRole(role) {
  const normalized = role.trim();
  if (!normalized) throw new Error("Proposal role cannot be empty; set TENT_ROLE before running tent propose.");
  if (normalized.includes("..") || /[\/\\\r\n]/.test(normalized)) throw new Error(`Invalid proposal role: ${role}`);
  return normalized;
}

// src/service/mutation-bus.ts
var MutationBus = class {
  constructor() {
    this.tails = /* @__PURE__ */ new Map();
  }
  async run(workspaceId, action) {
    const prev = this.tails.get(workspaceId) ?? Promise.resolve();
    let release;
    const gate = new Promise((resolve15) => {
      release = resolve15;
    });
    const chain = prev.catch(() => void 0).then(() => gate);
    this.tails.set(workspaceId, chain);
    await prev.catch(() => void 0);
    try {
      return await action();
    } finally {
      release();
      if (this.tails.get(workspaceId) === chain) {
        this.tails.delete(workspaceId);
      }
    }
  }
};

// src/service/task-lifecycle-flight.ts
var queue = new MutationBus();
var key = (ws, path23) => `${ws}\0${path23}`;
function runTaskLifecycle(workspaceId, taskPath, action) {
  return queue.run(key(workspaceId, taskPath), action);
}

// src/core/workspace.ts
import * as nodePath3 from "node:path";
import * as nodeFs from "node:fs/promises";
import { spawn } from "node:child_process";
async function findIntegratedCommit(workspace, sourceRef, targetBranch) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const full = await fullRef(root, sourceRef);
  const ancestor = await findAncestorIntegration(root, full, targetBranch);
  if (ancestor) return { integratedRef: full, reason: "ancestor" };
  const prior = await findCherryPick(root, full, targetBranch);
  if (prior) return { integratedRef: prior, reason: "cherry-pick" };
  return void 0;
}
async function findExactCompletedIntegrationAtTip(contract, refs, expectedTargetHead) {
  const commits = [...new Set(refs.map((ref) => ref.trim()).filter(Boolean))];
  const expected = expectedTargetHead.trim();
  if (commits.length === 0 || !expected) return void 0;
  const root = nodePath3.resolve(contract.workspace);
  await assertGitWorkspace(root);
  if (!await gitOk(root, ["cat-file", "-e", `${expected}^{commit}`])) return void 0;
  const targetRef = `refs/heads/${contract.targetBranch}`;
  const current = (await git(root, ["rev-parse", targetRef])).trim();
  if (!current || current === expected) return void 0;
  const resolved = [];
  for (const sourceRef of commits) {
    if (!await gitOk(root, ["cat-file", "-e", `${sourceRef}^{commit}`])) return void 0;
    resolved.push(await fullRef(root, sourceRef));
  }
  const fastForwardRef = await completeFastForwardRef(
    root,
    expected,
    resolved,
    contract.branch
  );
  if (fastForwardRef === current) {
    return {
      targetHead: current,
      mode: "fast-forward",
      integratedRefs: [...resolved]
    };
  }
  if (!await gitOk(root, ["merge-base", "--is-ancestor", expected, current])) {
    return void 0;
  }
  const firstParentRange = (await git(root, [
    "rev-list",
    "--first-parent",
    "--reverse",
    `${expected}..${current}`
  ])).split(/\r?\n/).map((ref) => ref.trim()).filter(Boolean);
  const integratedRefs = [];
  for (const sourceRef of resolved) {
    if (await gitOk(root, ["merge-base", "--is-ancestor", sourceRef, expected])) {
      continue;
    }
    const integratedRef = await findCherryPick(root, sourceRef, contract.targetBranch);
    if (!integratedRef) return void 0;
    integratedRefs.push(integratedRef);
  }
  if (integratedRefs.length === 0 || integratedRefs.length !== firstParentRange.length || integratedRefs.some((ref, index2) => ref !== firstParentRange[index2]) || integratedRefs.at(-1) !== current) {
    return void 0;
  }
  return {
    targetHead: current,
    mode: "cherry-pick",
    integratedRefs
  };
}
async function readRoleBranchTip(workspace, branch) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const name = branch.trim();
  if (!name) throw new Error("Role branch name is required.");
  const ref = (await git(root, ["rev-parse", `refs/heads/${name}`])).trim();
  if (!ref) throw new Error(`Cannot read role branch tip: ${name}.`);
  return ref;
}
async function isGitWorkspace(workspace) {
  try {
    await assertGitWorkspace(nodePath3.resolve(workspace));
    return true;
  } catch {
    return false;
  }
}
async function ensureRoleWorkspaceIfGit(workspace, role) {
  if (!await isGitWorkspace(workspace)) return void 0;
  return ensureRoleWorkspace(workspace, role);
}
async function ensureRoleWorkspace(workspace, role) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const targetBranch = await resolveTargetBranch(root);
  const roleSlug = safeComponent(role);
  const branch = `tent-role/${roleSlug}`;
  const worktree = nodePath3.join(
    nodePath3.dirname(root),
    `${nodePath3.basename(root)}-worktrees`,
    roleSlug
  );
  const existing = await worktreeForBranch(root, branch);
  if (existing) {
    const wt = await nodeFs.realpath(nodePath3.resolve(existing));
    const baseCommit2 = await readRoleBranchTip(root, branch);
    return { workspace: root, worktree: wt, branch, targetBranch, baseCommit: baseCommit2 };
  }
  if (await pathExists(worktree)) {
    throw new Error(`Role worktree path exists but is not registered to ${branch}: ${worktree}.`);
  }
  const branchExists = await gitOk(root, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`]);
  if (branchExists) {
    await git(root, ["worktree", "add", worktree, branch]);
  } else {
    await git(root, ["worktree", "add", "-b", branch, worktree, targetBranch]);
  }
  const baseCommit = await readRoleBranchTip(root, branch);
  return {
    workspace: root,
    worktree: await nodeFs.realpath(worktree),
    branch,
    targetBranch,
    baseCommit
  };
}
async function ensureTaskWorkspaceIfGit(workspace, taskId, options = {}) {
  if (!await isGitWorkspace(workspace)) return void 0;
  return ensureTaskWorkspace(workspace, taskId, options);
}
function taskWorktreeBranchName(taskId) {
  const id = taskId.trim();
  if (!id) throw new Error("Task id is required for task-scoped workspace lane.");
  return `tent-task/${safeComponent(id)}`;
}
function taskWorktreeDirectoryName(taskId) {
  const id = taskId.trim();
  if (!id) throw new Error("Task id is required for task-scoped workspace lane.");
  return `task-${safeComponent(id)}`;
}
function expectedTaskWorktreePath(workspaceRoot, taskId) {
  const root = nodePath3.resolve(workspaceRoot);
  return nodePath3.join(
    nodePath3.dirname(root),
    `${nodePath3.basename(root)}-worktrees`,
    taskWorktreeDirectoryName(taskId)
  );
}
async function ensureTaskWorkspace(workspace, taskId, options = {}) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const id = taskId.trim();
  if (!id) throw new Error("Task id is required for task-scoped workspace lane.");
  const requestedTarget = options.targetBranch?.trim();
  const targetBranch = requestedTarget || await resolveTargetBranch(root);
  if (requestedTarget && !await gitOk(root, ["show-ref", "--verify", "--quiet", `refs/heads/${targetBranch}`])) {
    throw new Error(`Task workspace target branch does not exist: ${targetBranch}.`);
  }
  const branch = taskWorktreeBranchName(id);
  const worktree = expectedTaskWorktreePath(root, id);
  const existing = await worktreeForBranch(root, branch);
  if (existing) {
    const wt = await nodeFs.realpath(nodePath3.resolve(existing));
    const baseCommit2 = await readRoleBranchTip(root, branch);
    return {
      workspace: root,
      worktree: wt,
      branch,
      targetBranch,
      baseCommit: baseCommit2
    };
  }
  if (await pathExists(worktree)) {
    throw new Error(`Task worktree path exists but is not registered to ${branch}: ${worktree}.`);
  }
  const branchExists = await gitOk(root, ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`]);
  if (branchExists) {
    await git(root, ["worktree", "add", worktree, branch]);
  } else {
    await git(root, ["worktree", "add", "-b", branch, worktree, targetBranch]);
  }
  const baseCommit = await readRoleBranchTip(root, branch);
  return {
    workspace: root,
    worktree: await nodeFs.realpath(worktree),
    branch,
    targetBranch,
    baseCommit
  };
}
async function inspectWorktreeDirtiness(worktree) {
  const cwd = nodePath3.resolve(worktree);
  const raw = (await git(cwd, ["status", "--porcelain"])).replace(/\r\n/g, "\n").trim();
  const lines = raw ? raw.split("\n").map((line) => line.trimEnd()).filter(Boolean) : [];
  let trackedDirty = false;
  let untrackedDirty = false;
  for (const line of lines) {
    if (line.startsWith("??") || line.startsWith("!")) {
      untrackedDirty = true;
    } else {
      trackedDirty = true;
    }
  }
  const sampleLines = lines.slice(0, 8);
  const sample = sampleLines.join("\n") + (lines.length > sampleLines.length ? `
\u2026(+${lines.length - sampleLines.length} more)` : "");
  return {
    dirty: lines.length > 0,
    worktree: cwd,
    trackedDirty,
    untrackedDirty,
    sample,
    changeCount: lines.length
  };
}
async function resolveIntegrationTargetLockIdentity(workspaceOrWorktree, targetBranch) {
  const cwd = nodePath3.resolve(workspaceOrWorktree);
  const branch = targetBranch.trim();
  if (!branch) {
    throw new Error("resolveIntegrationTargetLockIdentity requires a non-empty targetBranch");
  }
  let commonRaw = "";
  try {
    commonRaw = (await git(cwd, ["rev-parse", "--path-format=absolute", "--git-common-dir"])).trim();
  } catch {
    commonRaw = (await git(cwd, ["rev-parse", "--git-common-dir"])).trim();
  }
  if (!commonRaw) {
    throw new Error(`Cannot resolve git-common-dir for integration lock at ${cwd}`);
  }
  const commonResolved = nodePath3.isAbsolute(commonRaw) ? commonRaw : nodePath3.resolve(cwd, commonRaw);
  let gitCommonDir = commonResolved;
  try {
    gitCommonDir = await nodeFs.realpath(commonResolved);
  } catch {
    gitCommonDir = commonResolved;
  }
  const want = branch.startsWith("refs/") ? branch : `refs/heads/${branch}`;
  let targetRef = "";
  try {
    targetRef = (await git(cwd, ["rev-parse", "--verify", "--symbolic-full-name", want])).trim();
  } catch {
    targetRef = "";
  }
  if (!targetRef) {
    await git(cwd, ["rev-parse", "--verify", `${want}^{commit}`]);
    targetRef = want.startsWith("refs/") ? want : `refs/heads/${branch}`;
  }
  if (!targetRef.startsWith("refs/")) {
    throw new Error(
      `Integration target ref did not fully resolve for lock identity: branch=${branch} got=${targetRef}`
    );
  }
  return { gitCommonDir, targetRef };
}
async function integrateWorkspaceCommits(contract, refs) {
  const commits = [...new Set(refs.map((ref) => ref.trim()).filter(Boolean))];
  if (commits.length === 0) return [];
  const root = contract.workspace;
  const target = contract.targetBranch;
  const integrationCwd = await resolveIntegrationCwd(root, target);
  const current = (await git(integrationCwd, ["branch", "--show-current"])).trim();
  if (current !== target) {
    throw new Error(
      `No worktree has ${target} checked out for integration; found current branch ${current || "(detached)"} at ${integrationCwd}. Never auto-switch branches \u2014 ensure the target lane worktree exists and stays on ${target}.`
    );
  }
  const dirty = (await git(integrationCwd, ["status", "--porcelain"])).trim();
  if (dirty) {
    throw new Error(
      `Integration worktree has uncommitted changes; cannot integrate commits (${integrationCwd}).`
    );
  }
  const originalRef = (await git(root, ["rev-parse", `refs/heads/${target}`])).trim();
  let ownedTip = originalRef;
  const resolved = [];
  for (const sourceRef of commits) {
    await git(root, ["cat-file", "-e", `${sourceRef}^{commit}`]);
    resolved.push({ sourceRef, fullRef: await fullRef(root, sourceRef) });
  }
  const fastForwardRef = await completeFastForwardRef(
    root,
    originalRef,
    resolved.map((item) => item.fullRef),
    contract.branch
  );
  if (fastForwardRef) {
    try {
      await git(integrationCwd, ["merge", "--ff-only", fastForwardRef]);
      ownedTip = (await git(integrationCwd, ["rev-parse", "HEAD"])).trim();
      return resolved.map(({ sourceRef, fullRef: integratedRef }) => ({
        sourceRef,
        integratedRef,
        alreadyIntegrated: false
      }));
    } catch (error) {
      await rollbackIntegration(integrationCwd, originalRef, target, ownedTip, error);
    }
  }
  const results = [];
  try {
    for (const { sourceRef } of resolved) {
      const ancestor = await findAncestorIntegration(root, sourceRef, target);
      if (ancestor) {
        results.push({ sourceRef, integratedRef: ancestor, alreadyIntegrated: true });
        continue;
      }
      const prior = await findCherryPick(root, sourceRef, target);
      if (prior) {
        results.push({ sourceRef, integratedRef: prior, alreadyIntegrated: true });
        continue;
      }
      await git(integrationCwd, ["cherry-pick", "-x", sourceRef]);
      const integratedRef = (await git(integrationCwd, ["rev-parse", "HEAD"])).trim();
      ownedTip = integratedRef;
      results.push({ sourceRef, integratedRef, alreadyIntegrated: false });
    }
  } catch (error) {
    await rollbackIntegration(integrationCwd, originalRef, target, ownedTip, error);
  }
  return results;
}
async function resolveIntegrationCwd(root, targetBranch) {
  const mainCurrent = (await git(root, ["branch", "--show-current"])).trim();
  if (mainCurrent === targetBranch) {
    return root;
  }
  const existing = await worktreeForBranch(root, targetBranch);
  if (existing) {
    const wt = await nodeFs.realpath(nodePath3.resolve(existing));
    const wtCurrent = (await git(wt, ["branch", "--show-current"])).trim();
    if (wtCurrent === targetBranch) return wt;
    throw new Error(
      `Worktree for ${targetBranch} exists at ${wt} but current branch is ${wtCurrent || "(detached)"}; never auto-switch.`
    );
  }
  throw new Error(
    `No worktree has ${targetBranch} checked out. Main workspace is on ${mainCurrent || "(detached)"}. For sub tasks ensure the dispatcher role lane (tent-role/<dispatcher>) exists.`
  );
}
async function listRoleCommitsSince(contract, base) {
  const since = base.trim();
  if (!since) throw new Error("listRoleCommitsSince requires a non-empty base SHA.");
  const branchRef = `refs/heads/${contract.branch}`;
  const fullBase = await fullRef(contract.workspace, since);
  if (!await gitOk(contract.workspace, ["merge-base", "--is-ancestor", fullBase, branchRef])) {
    throw new Error(
      `Role branch ${contract.branch} no longer descends from task baseline ${fullBase}.`
    );
  }
  const output = await git(contract.workspace, [
    "log",
    `${fullBase}..${branchRef}`,
    "--format=%H%x09%h%x09%s"
  ]);
  return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
    const [ref = "", shortRef = "", ...subjectParts] = line.split("	");
    return { ref, shortRef, subject: subjectParts.join("	") };
  }).filter((item) => item.ref && item.shortRef);
}
async function listPendingRoleCommits(contract, base) {
  const candidates = await listRoleCommitsSince(contract, base);
  const pending = [];
  for (const item of candidates) {
    const integrated = await findIntegratedCommit(
      contract.workspace,
      item.ref,
      contract.targetBranch
    );
    if (!integrated) pending.push(item);
  }
  return pending.reverse();
}
async function listExecutorLaneCommitsWithParents(workspace, baseCommit, tipCommit) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const base = (await fullRef(root, baseCommit.trim())).trim();
  const tip = (await fullRef(root, tipCommit.trim())).trim();
  if (base === tip) return [];
  const output = await git(root, [
    "rev-list",
    "--parents",
    "--reverse",
    `${base}..${tip}`
  ]);
  return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
    const parts = line.split(/\s+/).map((p) => p.trim()).filter(Boolean);
    const sha = parts[0] || "";
    const parents = parts.slice(1);
    return { sha, parents };
  }).filter((c) => c.sha);
}
async function assertOrdinaryExecutorLaneHistoryInGit(input) {
  const root = nodePath3.resolve(input.workspace);
  await assertGitWorkspace(root);
  const base = input.baseCommit?.trim();
  if (!base) {
    throw new ExecutorLaneHistoryError(
      "MISSING_BASE",
      "Executor lane history gate requires recorded baseCommit (fail-loud; no ready TaskResult)."
    );
  }
  let tip = input.tipCommit?.trim() || "";
  if (!tip) {
    const branch = input.branch?.trim();
    if (!branch) {
      throw new ExecutorLaneHistoryError(
        "MISSING_TIP",
        "Executor lane history gate requires tip commit or branch."
      );
    }
    tip = (await git(root, ["rev-parse", `refs/heads/${branch}`])).trim();
  }
  const fullBase = await fullRef(root, base);
  const fullTip = await fullRef(root, tip);
  const commits = await listExecutorLaneCommitsWithParents(root, fullBase, fullTip);
  assertOrdinaryExecutorLaneHistory({
    baseCommit: fullBase,
    tipCommit: fullTip,
    commits
  });
}
var SubmitCommitLaneError = class extends Error {
  constructor(code, message2, details) {
    super(message2);
    this.name = "SubmitCommitLaneError";
    this.code = code;
    this.details = details;
  }
};
async function resolveSubmitCommitsInExecutorLane(input) {
  const refs = [...new Set(input.commits.map((c) => c.trim()).filter(Boolean))];
  if (refs.length === 0) return [];
  const root = nodePath3.resolve(input.workspace);
  await assertGitWorkspace(root);
  const baseRaw = input.baseCommit?.trim() || "";
  if (!baseRaw) {
    throw new SubmitCommitLaneError(
      "MISSING_BASE",
      "task.submit commits[] require recorded baseCommit (fail-loud; no ready TaskResult)."
    );
  }
  const branch = input.branch?.trim() || "";
  if (!branch) {
    throw new SubmitCommitLaneError(
      "MISSING_BRANCH",
      "task.submit commits[] require recorded executor branch (fail-loud; no ready TaskResult)."
    );
  }
  const fullBase = await fullRef(root, baseRaw);
  const branchRef = `refs/heads/${branch}`;
  if (!await gitOk(root, ["show-ref", "--verify", "--quiet", branchRef])) {
    throw new SubmitCommitLaneError(
      "MISSING_BRANCH",
      `task.submit commits[] executor branch missing: ${branch} (no ready TaskResult).`,
      { branch }
    );
  }
  const rangeOutput = await git(root, ["rev-list", "--reverse", `${fullBase}..${branchRef}`]);
  const laneRange = new Set(
    rangeOutput.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
  );
  const canonical = [];
  for (const sourceRef of refs) {
    const isCommit = await gitOk(root, ["cat-file", "-e", `${sourceRef}^{commit}`]);
    if (!isCommit) {
      const exists = await gitOk(root, ["cat-file", "-e", sourceRef]);
      throw new SubmitCommitLaneError(
        exists ? "NOT_A_COMMIT" : "MISSING_COMMIT",
        exists ? `task.submit commits[] entry is not a commit object: ${sourceRef} (no ready TaskResult; Git untouched).` : `task.submit commits[] entry does not resolve in workspace Git: ${sourceRef} (no ready TaskResult; Git untouched).`,
        { commit: sourceRef }
      );
    }
    const full = await fullRef(root, sourceRef);
    if (full === fullBase) {
      throw new SubmitCommitLaneError(
        "BASE_COMMIT",
        `task.submit commits[] must not list recorded baseCommit ${fullBase} itself (no ready TaskResult; Git untouched).`,
        { commit: full, baseCommit: fullBase, branch }
      );
    }
    if (!laneRange.has(full)) {
      const reachableFromBranch = await gitOk(root, [
        "merge-base",
        "--is-ancestor",
        full,
        branchRef
      ]);
      if (!reachableFromBranch) {
        throw new SubmitCommitLaneError(
          "NOT_REACHABLE_FROM_BRANCH",
          `task.submit commits[] entry ${full} is not reachable from executor branch ${branch} (foreign/sibling/unrelated ancestry; no ready TaskResult; Git untouched).`,
          { commit: full, baseCommit: fullBase, branch }
        );
      }
      throw new SubmitCommitLaneError(
        "NOT_IN_LANE_RANGE",
        `task.submit commits[] entry ${full} is not in exact range ${fullBase}..${branch} (target-only, base history, or foreign to this task lane; no ready TaskResult; Git untouched).`,
        { commit: full, baseCommit: fullBase, branch }
      );
    }
    if (!await gitOk(root, ["merge-base", "--is-ancestor", full, branchRef])) {
      throw new SubmitCommitLaneError(
        "NOT_REACHABLE_FROM_BRANCH",
        `task.submit commits[] entry ${full} failed branch reachability re-check for ${branch}.`,
        { commit: full, baseCommit: fullBase, branch }
      );
    }
    if (!canonical.includes(full)) canonical.push(full);
  }
  return canonical;
}
async function assertGitWorkspace(root) {
  const top = (await git(root, ["rev-parse", "--show-toplevel"])).trim();
  const [realTop, realRoot] = await Promise.all([
    nodeFs.realpath(nodePath3.resolve(top)),
    nodeFs.realpath(root)
  ]);
  if (!isSameWorkspaceRoot(realTop, realRoot)) {
    throw new Error(`Workspace must be a Git root: ${root}.`);
  }
}
function isSameWorkspaceRoot(realTop, realRoot, platform = process.platform) {
  const top = platform === "win32" ? realTop.toLowerCase() : realTop;
  const root = platform === "win32" ? realRoot.toLowerCase() : realRoot;
  return top === root;
}
async function resolveTargetBranch(root) {
  for (const name of ["main", "master"]) {
    if (await gitOk(root, ["show-ref", "--verify", "--quiet", `refs/heads/${name}`])) return name;
  }
  const current = (await git(root, ["branch", "--show-current"])).trim();
  if (!current) throw new Error("Cannot identify the workspace main branch.");
  return current;
}
async function worktreeForBranch(root, branch) {
  const output = await git(root, ["worktree", "list", "--porcelain"]);
  let currentPath = "";
  for (const line of output.split(/\r?\n/)) {
    if (line.startsWith("worktree ")) currentPath = line.slice("worktree ".length);
    if (line === `branch refs/heads/${branch}`) return currentPath;
  }
  return void 0;
}
async function findCherryPick(root, sourceRef, targetBranch) {
  const full = await fullRef(root, sourceRef);
  const sourcePatchId = await verbatimPatchId(root, full);
  if (!sourcePatchId) return void 0;
  const needle = `(cherry picked from commit ${full})`;
  const targetRef = `refs/heads/${targetBranch}`;
  const output = await git(root, ["log", targetRef, "--format=%H%x00%B%x00", "-n", "5000"]);
  const parts = output.split("\0");
  for (let i = 0; i + 1 < parts.length; i += 2) {
    const body = parts[i + 1] ?? "";
    if (!body.includes(needle)) continue;
    const candidate = parts[i]?.trim() ?? "";
    if (!candidate) continue;
    const candidatePatchId = await verbatimPatchId(root, candidate);
    if (candidatePatchId && candidatePatchId === sourcePatchId) return candidate;
  }
  return void 0;
}
async function verbatimPatchId(root, commitRef) {
  try {
    const patch = await git(root, [
      "show",
      "--format=",
      "--no-ext-diff",
      "--binary",
      commitRef
    ]);
    const output = await git(root, ["patch-id", "--verbatim"], patch);
    const patchId = output.trim().split(/\s+/, 1)[0]?.trim().toLowerCase() ?? "";
    return /^[0-9a-f]{40,64}$/.test(patchId) ? patchId : void 0;
  } catch {
    return void 0;
  }
}
async function findAncestorIntegration(root, sourceRef, targetBranch) {
  const targetRef = `refs/heads/${targetBranch}`;
  const full = await fullRef(root, sourceRef);
  if (await gitOk(root, ["merge-base", "--is-ancestor", full, targetRef])) {
    return full;
  }
  return void 0;
}
async function completeFastForwardRef(root, targetRef, commits, sourceBranch) {
  const lastRef = commits.at(-1);
  if (!lastRef || lastRef === targetRef) return void 0;
  if (!await gitOk(root, ["merge-base", "--is-ancestor", targetRef, lastRef])) return void 0;
  const sourceRef = `refs/heads/${sourceBranch}`;
  if (!await gitOk(root, ["merge-base", "--is-ancestor", lastRef, sourceRef])) {
    return void 0;
  }
  if (commits.length === 1) {
    return lastRef;
  }
  const range = (await git(root, ["rev-list", "--reverse", `${targetRef}..${lastRef}`])).split(/\r?\n/).map((ref) => ref.trim()).filter(Boolean);
  if (range.length !== commits.length) return void 0;
  const supplied = new Set(commits);
  return range.every((ref) => supplied.has(ref)) ? lastRef : void 0;
}
async function rollbackIntegration(root, originalRef, targetBranch, ownedTip, cause) {
  return rollbackIntegrationCas({
    root,
    originalRef,
    targetBranch,
    ownedTip,
    cause
  });
}
async function rollbackIntegrationCas(input) {
  const root = nodePath3.resolve(input.root);
  const target = input.targetBranch.trim();
  const targetRef = `refs/heads/${target}`;
  const originalRef = input.originalRef.trim();
  const ownedTip = input.ownedTip.trim();
  const cause = input.cause;
  let currentTip = "";
  try {
    currentTip = (await git(root, ["rev-parse", targetRef])).trim();
  } catch (readError) {
    throw new Error(
      `Workspace integration conflicted; rollback could not read target ${target}: ${errorMessage(cause)}; read: ${errorMessage(readError)}`
    );
  }
  if (currentTip !== originalRef && currentTip !== ownedTip) {
    throw new Error(
      `Workspace integration conflicted; rollback refused because target ${target} advanced by another writer after this operation's write (owned ${ownedTip}, current ${currentTip}, original ${originalRef}); ref, worktree, and sequencer evidence preserved: ${errorMessage(cause)}`
    );
  }
  await git(root, ["cherry-pick", "--quit"]).catch(() => "");
  if (currentTip === ownedTip && currentTip !== originalRef) {
    try {
      await git(root, ["update-ref", targetRef, originalRef, ownedTip]);
    } catch (casError) {
      throw new Error(
        `Workspace integration conflicted; rollback CAS failed for target ${target} (owned ${ownedTip}, original ${originalRef}): ${errorMessage(cause)}; cas: ${errorMessage(casError)}`
      );
    }
    currentTip = originalRef;
  }
  try {
    await git(root, ["read-tree", "-u", "--reset", currentTip]);
    await git(root, ["checkout-index", "-a", "-f"]).catch(() => "");
  } catch (rollbackError) {
    try {
      await git(root, ["reset", "--hard", currentTip]);
    } catch (hardError) {
      throw new Error(
        `Workspace integration failed and rollback also failed: ${errorMessage(cause)}; reconcile: ${errorMessage(rollbackError)}; hard: ${errorMessage(hardError)}`
      );
    }
  }
  throw new Error(`Workspace integration conflicted and was rolled back: ${errorMessage(cause)}`);
}
function errorMessage(error) {
  return error instanceof Error ? error.message : String(error);
}
async function fullRef(root, ref) {
  return (await git(root, ["rev-parse", ref])).trim();
}
async function resolveCommitSha(workspace, commitish) {
  const root = nodePath3.resolve(workspace);
  await assertGitWorkspace(root);
  const raw = commitish.trim();
  if (!raw) throw new Error("resolveCommitSha requires a non-empty commitish.");
  const isCommit = await gitOk(root, ["cat-file", "-e", `${raw}^{commit}`]);
  if (!isCommit) {
    throw new Error(`Not a commit in workspace Git: ${raw}`);
  }
  return (await fullRef(root, raw)).trim();
}
function safeComponent(value) {
  const source = value.trim();
  const normalized = source.normalize("NFKC");
  let clean = normalized.replace(/[<>:"/\\|?*\x00-\x1f~^:[\]@{}]+/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^[.-]+|[.-]+$/g, "").slice(0, 40);
  const reserved = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(clean);
  if (reserved) clean = `role-${clean}`;
  if (!clean) return `role-${shortHash(source)}`;
  return clean !== normalized || normalized !== source || reserved ? `${clean}-${shortHash(source)}` : clean;
}
function shortHash(value) {
  let hash = 2166136261;
  for (const char of value) {
    hash ^= char.codePointAt(0) || 0;
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0).toString(36).padStart(6, "0").slice(0, 6);
}
async function pathExists(path23) {
  try {
    await nodeFs.access(path23);
    return true;
  } catch {
    return false;
  }
}
async function gitOk(cwd, args) {
  try {
    await git(cwd, args);
    return true;
  } catch {
    return false;
  }
}
function git(cwd, args, stdin) {
  return new Promise((resolve15, reject) => {
    const child = spawn("git", args, { cwd, windowsHide: true });
    let out = "";
    let err = "";
    child.stdout.on("data", (data) => out += data);
    child.stderr.on("data", (data) => err += data);
    child.on("close", (code) => {
      if (code === 0) resolve15(out);
      else reject(new Error(err.trim() || `git ${args.join(" ")} exit ${code}`));
    });
    child.on("error", reject);
    if (stdin !== void 0) child.stdin.end(stdin);
  });
}

// src/service/integration-target-flight.ts
var queue2 = new MutationBus();
function targetFlightKey(id) {
  return `${id.gitCommonDir}\0${id.targetRef}`;
}
async function runIntegrationTargetFlight(workspaceRoot, targetBranch, action) {
  const id = await resolveIntegrationTargetLockIdentity(workspaceRoot, targetBranch);
  return queue2.run(targetFlightKey(id), action);
}

// src/core/retention.ts
var DEFAULT_KEEP_TERMINAL_DAYS = 30;
var MAX_KEEP_TERMINAL_DAYS = 3650;
var MS_PER_DAY = 24 * 60 * 60 * 1e3;
var TERMINAL_RESULT_STATUSES = /* @__PURE__ */ new Set([
  "accepted",
  "rejected"
]);
var RetentionError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "RetentionError";
  }
};
function normalizeKeepTerminalTasksDays(raw) {
  if (raw === void 0 || raw === null) return DEFAULT_KEEP_TERMINAL_DAYS;
  if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw)) {
    throw new RetentionError(
      "INVALID_KEEP_DAYS",
      "keepTerminalTasksDays must be a non-negative integer"
    );
  }
  if (raw < 0) {
    throw new RetentionError(
      "INVALID_KEEP_DAYS",
      "keepTerminalTasksDays must be a non-negative integer"
    );
  }
  if (raw > MAX_KEEP_TERMINAL_DAYS) {
    throw new RetentionError(
      "INVALID_KEEP_DAYS",
      `keepTerminalTasksDays must be \u2264 ${MAX_KEEP_TERMINAL_DAYS}`
    );
  }
  return raw;
}
function isTerminalTaskState(state) {
  return TERMINAL_TASK_STATES.has(state);
}
function isPurgeableTaskResultStatus(status) {
  return TERMINAL_RESULT_STATUSES.has(status);
}
async function previewOperationalRetention(fs21, options = {}) {
  const keepTerminalTasksDays = normalizeKeepTerminalTasksDays(options.keepTerminalTasksDays);
  const nowMs = resolveNowMs(options.now);
  const cutoffMs = nowMs - keepTerminalTasksDays * MS_PER_DAY;
  const cutoff = new Date(cutoffMs).toISOString();
  const skipped = [];
  const warnings = [];
  const { tasks, skipped: taskSkipped } = await scanTasks(fs21);
  skipped.push(...taskSkipped);
  const { results, skipped: resultSkipped } = await scanResults(fs21);
  skipped.push(...resultSkipped);
  for (const s of skipped) {
    warnings.push(`skipped ${s.path}: ${s.reason}`);
  }
  const tasksById = /* @__PURE__ */ new Map();
  const taskIdCounts = /* @__PURE__ */ new Map();
  for (const t of tasks) {
    if (t.id) {
      tasksById.set(t.id, t);
      taskIdCounts.set(t.id, (taskIdCounts.get(t.id) ?? 0) + 1);
    }
  }
  const resultsByTaskId = /* @__PURE__ */ new Map();
  for (const d of results) {
    const list2 = resultsByTaskId.get(d.taskId) ?? [];
    list2.push(d);
    resultsByTaskId.set(d.taskId, list2);
  }
  const candidates = [];
  const claimedTaskResultPaths = /* @__PURE__ */ new Set();
  let pinnedTaskResultIds;
  try {
    const tent = await loadTent(fs21);
    pinnedTaskResultIds = collectReferencedTaskResultIds(tent);
  } catch (err) {
    throw new RetentionError(
      "PROVENANCE_PIN_SCAN_FAILED",
      `Output provenance pin scan failed; refusing retention preview/purge: ${err instanceof Error ? err.message : String(err)}`
    );
  }
  const pinnedTaskIds = /* @__PURE__ */ new Set();
  for (const d of results) {
    if (pinnedTaskResultIds.has(d.id) && d.taskId) pinnedTaskIds.add(d.taskId);
  }
  for (const task of tasks) {
    if (!isTerminalTaskState(task.state)) continue;
    if (isActiveTaskState(task.state)) continue;
    if (task.id && (taskIdCounts.get(task.id) ?? 0) > 1) {
      const message2 = `duplicate task id ${task.id}; refusing ambiguous retention group`;
      skipped.push({ path: task.path, reason: message2 });
      warnings.push(`skipped ${task.path}: ${message2}`);
      continue;
    }
    const related = task.id ? resultsByTaskId.get(task.id) ?? [] : [];
    const activityValues = [taskActivityMs(task), ...related.map(resultActivityMs)];
    if (activityValues.some((value) => value === void 0)) {
      if (keepTerminalTasksDays > 0) {
        skipped.push({
          path: task.path,
          reason: "task group has missing createdAt/updatedAt; not eligible while keepTerminalTasksDays > 0"
        });
        warnings.push(
          `skipped ${task.path}: task group has missing createdAt/updatedAt; not eligible while keepTerminalTasksDays > 0`
        );
        continue;
      }
    }
    const activityMs = Math.max(...activityValues.map((value) => value ?? 0));
    if (activityMs > cutoffMs) continue;
    const protectedResults = related.filter((d) => !isPurgeableTaskResultStatus(d.status));
    if (protectedResults.length > 0) {
      warnings.push(
        `task-group ${task.path} has ${protectedResults.length} non-terminal result(ies); refusing group purge`
      );
      continue;
    }
    const pinnedRelated = related.filter((d) => pinnedTaskResultIds.has(d.id));
    if (pinnedRelated.length > 0 || task.id !== void 0 && pinnedTaskIds.has(task.id)) {
      const pinIds = pinnedRelated.map((d) => d.id).join(",") || task.id || "";
      warnings.push(
        `task-group ${task.path} pinned by Output.resultId (${pinIds}); refusing group purge`
      );
      continue;
    }
    const ageDays = ageDaysFrom(activityMs, nowMs);
    const resultPaths = related.map((d) => d.path);
    for (const p of resultPaths) claimedTaskResultPaths.add(p);
    candidates.push({
      kind: "task-group",
      taskId: task.id,
      taskPath: task.path,
      taskState: task.state,
      resultPaths,
      ageDays,
      reason: `terminal task state=${task.state} ageDays=${ageDays} \u2265 keep=${keepTerminalTasksDays}`
    });
  }
  for (const d of results) {
    if (claimedTaskResultPaths.has(d.path)) continue;
    if (!isPurgeableTaskResultStatus(d.status)) continue;
    const parent = tasksById.get(d.taskId);
    if (parent) {
      continue;
    }
    if (pinnedTaskResultIds.has(d.id)) {
      warnings.push(
        `orphan result ${d.path} pinned by Output.resultId; refusing purge`
      );
      continue;
    }
    const activity = resultActivityMs(d);
    if (activity === void 0) {
      if (keepTerminalTasksDays > 0) {
        skipped.push({
          path: d.path,
          reason: "missing createdAt/updatedAt; not eligible while keepTerminalTasksDays > 0"
        });
        warnings.push(
          `skipped ${d.path}: missing createdAt/updatedAt; not eligible while keepTerminalTasksDays > 0`
        );
        continue;
      }
    }
    const activityMs = activity ?? 0;
    if (activityMs > cutoffMs) continue;
    const ageDays = ageDaysFrom(activityMs, nowMs);
    candidates.push({
      kind: "orphan-result",
      taskId: d.taskId,
      resultPaths: [d.path],
      ageDays,
      reason: `orphan non-ready result status=${d.status} ageDays=${ageDays} \u2265 keep=${keepTerminalTasksDays}`
    });
  }
  candidates.sort((a, b) => {
    const ap = a.taskPath || a.resultPaths[0] || "";
    const bp = b.taskPath || b.resultPaths[0] || "";
    return ap.localeCompare(bp);
  });
  let candidateTaskResultCount = 0;
  let candidateTaskCount = 0;
  for (const c of candidates) {
    if (c.kind === "task-group") candidateTaskCount += 1;
    candidateTaskResultCount += c.resultPaths.length;
  }
  return {
    keepTerminalTasksDays,
    cutoff,
    candidates,
    skipped,
    warnings,
    candidateTaskCount,
    candidateTaskResultCount
  };
}
async function purgeOperationalRetention(fs21, options = {}) {
  return withTentMutation(fs21, async () => {
    const preview = await previewOperationalRetention(fs21, options);
    const purgedTaskPaths = [];
    const purgedTaskResultPaths = [];
    for (const c of preview.candidates) {
      if (c.kind === "task-group" && c.taskPath) {
        try {
          const live = await loadTaskRecord(fs21, c.taskPath);
          if (!isTerminalTaskState(live.state) || isActiveTaskState(live.state)) {
            preview.warnings.push(
              `refused purge of ${c.taskPath}: state is ${live.state} (not terminal)`
            );
            continue;
          }
        } catch (err) {
          preview.warnings.push(
            `refused purge of ${c.taskPath}: ${err instanceof Error ? err.message : String(err)}`
          );
          continue;
        }
        let resultValidationFailed = false;
        for (const dp of c.resultPaths) {
          try {
            const liveD = await loadTaskResult(fs21, dp);
            if (!isPurgeableTaskResultStatus(liveD.status)) {
              preview.warnings.push(
                `refused purge of task group ${c.taskPath}: result ${dp} status=${liveD.status}`
              );
              resultValidationFailed = true;
              break;
            }
          } catch (err) {
            preview.warnings.push(
              `refused purge of task group ${c.taskPath}: ${err instanceof Error ? err.message : String(err)}`
            );
            resultValidationFailed = true;
            break;
          }
        }
        if (resultValidationFailed) continue;
        try {
          if (await fs21.exists(c.taskPath)) {
            await fs21.remove(c.taskPath);
            purgedTaskPaths.push(c.taskPath);
          }
        } catch (err) {
          preview.warnings.push(
            `failed to purge task ${c.taskPath}: ${err instanceof Error ? err.message : String(err)}`
          );
          continue;
        }
        for (const dp of c.resultPaths) {
          try {
            if (await fs21.exists(dp)) {
              await fs21.remove(dp);
              purgedTaskResultPaths.push(dp);
            }
          } catch (err) {
            preview.warnings.push(
              `failed to purge orphaned result ${dp}: ${err instanceof Error ? err.message : String(err)}`
            );
          }
        }
        continue;
      }
      if (c.kind === "orphan-result") {
        for (const dp of c.resultPaths) {
          try {
            const liveD = await loadTaskResult(fs21, dp);
            if (!isPurgeableTaskResultStatus(liveD.status)) {
              preview.warnings.push(
                `refused purge of result ${dp}: status=${liveD.status}`
              );
              continue;
            }
            if (await fs21.exists(dp)) {
              await fs21.remove(dp);
              purgedTaskResultPaths.push(dp);
            }
          } catch (err) {
            preview.warnings.push(
              `failed to purge result ${dp}: ${err instanceof Error ? err.message : String(err)}`
            );
          }
        }
      }
    }
    const deletedCount = purgedTaskPaths.length + purgedTaskResultPaths.length;
    return {
      ...preview,
      purged: {
        taskPaths: purgedTaskPaths,
        resultPaths: purgedTaskResultPaths
      },
      deletedCount
    };
  });
}
async function scanTasks(fs21) {
  const tasks = [];
  const skipped = [];
  if (!await fs21.exists("temp")) return { tasks, skipped };
  for (const namespace of await fs21.listDir("temp")) {
    if (!namespace.isDir || namespace.name !== "roles" && namespace.name !== "sessions") continue;
    const namespaceRoot = join("temp", namespace.name);
    for (const ownerEntry of await fs21.listDir(namespaceRoot)) {
      if (!ownerEntry.isDir) continue;
      if (!isSafeRoleSegment(ownerEntry.name)) {
        skipped.push({
          path: join(namespaceRoot, ownerEntry.name),
          reason: `unsafe ${namespace.name} directory name`
        });
        continue;
      }
      await scanTaskDir(fs21, join(namespaceRoot, ownerEntry.name, "tasks"), tasks, skipped);
    }
  }
  return { tasks, skipped };
}
async function scanTaskDir(fs21, taskDir, tasks, skipped) {
  if (!await fs21.exists(taskDir)) return;
  for (const entry of await fs21.listDir(taskDir)) {
    if (entry.isDir || !entry.name.endsWith(".md")) continue;
    const path23 = join(taskDir, entry.name);
    try {
      tasks.push(await loadTaskRecord(fs21, path23));
    } catch (err) {
      skipped.push({
        path: path23,
        reason: err instanceof Error ? err.message : String(err)
      });
    }
  }
}
async function scanResults(fs21) {
  const results = [];
  const skipped = [];
  if (!await fs21.exists("temp")) return { results, skipped };
  for (const namespace of await fs21.listDir("temp")) {
    if (!namespace.isDir || namespace.name !== "roles" && namespace.name !== "sessions") continue;
    const namespaceRoot = join("temp", namespace.name);
    for (const ownerEntry of await fs21.listDir(namespaceRoot)) {
      if (!ownerEntry.isDir) continue;
      if (!isSafeRoleSegment(ownerEntry.name)) {
        skipped.push({
          path: join(namespaceRoot, ownerEntry.name),
          reason: `unsafe ${namespace.name} directory name`
        });
        continue;
      }
      await scanTaskResultDir(
        fs21,
        join(namespaceRoot, ownerEntry.name, "results"),
        results,
        skipped
      );
    }
  }
  return { results, skipped };
}
async function scanTaskResultDir(fs21, dir, results, skipped) {
  if (!await fs21.exists(dir)) return;
  for (const entry of await fs21.listDir(dir)) {
    if (entry.isDir || !entry.name.endsWith(".md")) continue;
    const path23 = join(dir, entry.name);
    try {
      results.push(await loadTaskResult(fs21, path23));
    } catch (err) {
      skipped.push({
        path: path23,
        reason: err instanceof Error ? err.message : String(err)
      });
    }
  }
}
function isSafeRoleSegment(name) {
  if (!name || name === "." || name === "..") return false;
  if (/[\\/]/.test(name)) return false;
  if (name.includes("\0")) return false;
  return true;
}
function taskActivityMs(task) {
  return parseIsoMs(task.updatedAt) ?? parseIsoMs(task.createdAt);
}
function resultActivityMs(d) {
  return parseIsoMs(d.createdAt);
}
function parseIsoMs(value) {
  if (!value) return void 0;
  const ms = Date.parse(value);
  if (!Number.isFinite(ms)) return void 0;
  return ms;
}
function resolveNowMs(now) {
  if (now === void 0) return Date.now();
  if (now instanceof Date) {
    const ms2 = now.getTime();
    if (!Number.isFinite(ms2)) throw new RetentionError("INVALID_KEEP_DAYS", "Invalid now Date");
    return ms2;
  }
  const ms = Date.parse(now);
  if (!Number.isFinite(ms)) {
    throw new RetentionError("INVALID_KEEP_DAYS", "Invalid now ISO timestamp");
  }
  return ms;
}
function ageDaysFrom(activityMs, nowMs) {
  const delta = Math.max(0, nowMs - activityMs);
  return Math.floor(delta / MS_PER_DAY);
}

// src/core/task-worktree-reclaim.ts
import * as nodeFs2 from "node:fs/promises";
import * as nodePath4 from "node:path";
import { spawn as spawn2 } from "node:child_process";
function isTaskWorktreeReclaimTerminalState(state) {
  return TERMINAL_TASK_STATES.has(state) && !isActiveTaskState(state);
}
function isTaskScopedWorktreeLane(task) {
  return task.branch?.startsWith("tent-task/") === true;
}
async function evaluateTaskWorktreeReclaim(input) {
  const task = input.task;
  const base = {
    taskId: task.id,
    taskPath: task.path,
    taskState: task.state,
    workspace: task.workspace,
    worktree: task.worktree,
    branch: task.branch,
    targetBranch: task.targetBranch
  };
  if (!isTaskScopedWorktreeLane(task)) {
    return {
      ...base,
      eligible: false,
      code: "NOT_APPLICABLE",
      reason: "Role worktrees are durable integration lanes; Task worktree reclaim applies only to recorded tent-task/* lanes."
    };
  }
  const hasLane = Boolean(
    task.worktree?.trim() || task.branch?.trim() || task.workspace?.trim()
  );
  if (!hasLane) {
    return {
      ...base,
      eligible: false,
      code: "NOT_APPLICABLE",
      reason: "Task has no recorded Git workspace lane (pure Tent / docs task)."
    };
  }
  if (!isTaskWorktreeReclaimTerminalState(task.state)) {
    return {
      ...base,
      eligible: false,
      code: "NOT_TERMINAL",
      reason: `Task state=${task.state} is not terminal for worktree reclaim (need accepted|rejected|interrupted|failed).`
    };
  }
  const taskId = (task.id || "").trim();
  if (!taskId) {
    return {
      ...base,
      eligible: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: "Task envelope is missing a stable task id; refusing worktree reclaim."
    };
  }
  const workspaceRoot = nodePath4.resolve(input.workspaceRoot);
  if (!await isGitWorkspace(workspaceRoot)) {
    return {
      ...base,
      eligible: false,
      code: "NOT_APPLICABLE",
      reason: "Mounted workspace is not a Git root; no Task worktree to reclaim."
    };
  }
  if (task.workspace?.trim()) {
    const claimed = nodePath4.resolve(task.workspace);
    if (!isSameWorkspaceRoot(claimed, workspaceRoot)) {
      return {
        ...base,
        eligible: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `Task envelope workspace mismatch: envelope=${task.workspace} mounted=${workspaceRoot}`,
        details: { claimedWorkspace: claimed, mountedWorkspace: workspaceRoot }
      };
    }
  }
  const expectedBranch = taskWorktreeBranchName(taskId);
  const expectedPath = expectedTaskWorktreePath(workspaceRoot, taskId);
  if (task.branch?.trim() && task.branch.trim() !== expectedBranch) {
    return {
      ...base,
      eligible: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Task branch ${task.branch} is not the expected Task lane ${expectedBranch}.`,
      details: { expectedBranch }
    };
  }
  if (task.branch?.trim().startsWith("tent-role/")) {
    return {
      ...base,
      eligible: false,
      code: "NOT_APPLICABLE",
      reason: "Envelope branch is a durable tent-role/* lane; refusing Task worktree reclaim."
    };
  }
  let resolvedWorktree;
  if (task.worktree?.trim()) {
    resolvedWorktree = nodePath4.resolve(task.worktree.trim());
    const expectedResolved = nodePath4.resolve(expectedPath);
    let same = isSameWorkspaceRoot(resolvedWorktree, expectedResolved);
    if (!same) {
      try {
        const [realClaimed, realExpected] = await Promise.all([
          nodeFs2.realpath(resolvedWorktree).catch(() => resolvedWorktree),
          nodeFs2.realpath(expectedResolved).catch(() => expectedResolved)
        ]);
        same = isSameWorkspaceRoot(realClaimed, realExpected);
      } catch {
        same = false;
      }
    }
    if (!same) {
      return {
        ...base,
        eligible: false,
        code: "EXTERNAL_OR_UNEXPECTED_PATH",
        reason: `Task worktree path is not the exact expected Task lane directory.`,
        details: {
          envelopeWorktree: resolvedWorktree,
          expectedWorktree: expectedResolved
        }
      };
    }
  } else {
    resolvedWorktree = nodePath4.resolve(expectedPath);
  }
  if (isSameWorkspaceRoot(resolvedWorktree, workspaceRoot)) {
    return {
      ...base,
      eligible: false,
      code: "EXTERNAL_OR_UNEXPECTED_PATH",
      reason: "Refusing to reclaim the main workspace root as a Task worktree.",
      worktree: resolvedWorktree
    };
  }
  const worktreesRoot = nodePath4.resolve(
    nodePath4.dirname(workspaceRoot),
    `${nodePath4.basename(workspaceRoot)}-worktrees`
  );
  const rel = nodePath4.relative(worktreesRoot, resolvedWorktree);
  if (!rel || rel.startsWith("..") || nodePath4.isAbsolute(rel) || rel.split(nodePath4.sep).length !== 1 || !rel.startsWith("task-")) {
    return {
      ...base,
      eligible: false,
      code: "EXTERNAL_OR_UNEXPECTED_PATH",
      reason: `Worktree path is outside the Task worktrees root or is not a task-* directory: ${resolvedWorktree}`,
      worktree: resolvedWorktree,
      details: { worktreesRoot }
    };
  }
  const registration = await inspectWorktreeRegistration(workspaceRoot, resolvedWorktree);
  const branchRegistration = await worktreePathForBranch(workspaceRoot, expectedBranch);
  const pathExistsOnDisk = await pathExists2(resolvedWorktree);
  const registeredHere = registration.registered && registration.branch === expectedBranch;
  if (!pathExistsOnDisk && !registration.registered && !branchRegistration) {
    return {
      ...base,
      eligible: true,
      code: "ALREADY_GONE",
      reason: "Task worktree directory and Git registration are already absent (idempotent no-op).",
      worktree: resolvedWorktree,
      branch: expectedBranch
    };
  }
  if (branchRegistration) {
    const branchPath = nodePath4.resolve(branchRegistration);
    if (!isSameWorkspaceRoot(branchPath, resolvedWorktree)) {
      let same = false;
      try {
        const [a, b] = await Promise.all([
          nodeFs2.realpath(branchPath).catch(() => branchPath),
          nodeFs2.realpath(resolvedWorktree).catch(() => resolvedWorktree)
        ]);
        same = isSameWorkspaceRoot(a, b);
      } catch {
        same = false;
      }
      if (!same) {
        return {
          ...base,
          eligible: false,
          code: "CONFLICTED_REGISTRATION",
          reason: `Branch ${expectedBranch} is registered at a different worktree path.`,
          worktree: resolvedWorktree,
          branch: expectedBranch,
          details: { registeredPath: branchPath }
        };
      }
    }
  }
  if (registration.registered) {
    if (registration.branch && registration.branch !== expectedBranch) {
      return {
        ...base,
        eligible: false,
        code: "CONFLICTED_REGISTRATION",
        reason: `Worktree at ${resolvedWorktree} is registered to ${registration.branch}, expected ${expectedBranch}.`,
        worktree: resolvedWorktree,
        branch: expectedBranch,
        details: { registeredBranch: registration.branch }
      };
    }
    if (!registration.branch) {
      return {
        ...base,
        eligible: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `Worktree at ${resolvedWorktree} is registered without a branch (detached/ambiguous); refusing reclaim.`,
        worktree: resolvedWorktree
      };
    }
  } else if (pathExistsOnDisk) {
    return {
      ...base,
      eligible: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Path ${resolvedWorktree} exists on disk but is not a registered Git worktree for ${expectedBranch}.`,
      worktree: resolvedWorktree,
      branch: expectedBranch
    };
  }
  if (pathExistsOnDisk) {
    try {
      const dirty = await inspectWorktreeDirtiness(resolvedWorktree);
      if (dirty.dirty) {
        return {
          ...base,
          eligible: false,
          code: "DIRTY",
          reason: `Task worktree has uncommitted changes (${dirty.changeCount}); refusing reclaim.`,
          worktree: resolvedWorktree,
          branch: expectedBranch,
          details: {
            trackedDirty: dirty.trackedDirty,
            untrackedDirty: dirty.untrackedDirty,
            changeCount: dirty.changeCount,
            dirtySample: dirty.sample
          }
        };
      }
    } catch (err) {
      return {
        ...base,
        eligible: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `Failed to inspect worktree dirtiness: ${err instanceof Error ? err.message : String(err)}`,
        worktree: resolvedWorktree,
        branch: expectedBranch
      };
    }
  }
  if (task.state === "accepted") {
    const targetBranch = (task.targetBranch || "").trim();
    const results = input.results ?? [];
    const settle = await evaluateAcceptedSettle({
      workspaceRoot,
      targetBranch,
      taskId,
      taskBranch: expectedBranch,
      roleBranchBase: task.roleBranchBase,
      results
    });
    if (!settle.ok) {
      return {
        ...base,
        eligible: false,
        code: settle.code,
        reason: settle.reason,
        worktree: resolvedWorktree,
        branch: expectedBranch,
        targetBranch: targetBranch || void 0,
        details: settle.details
      };
    }
  }
  const ready = (input.results ?? []).filter((d) => d.status === "ready");
  if (ready.length > 0) {
    return {
      ...base,
      eligible: false,
      code: "UNINTEGRATED",
      reason: `Task still has ${ready.length} non-terminal result record(s); refusing worktree reclaim.`,
      worktree: resolvedWorktree,
      branch: expectedBranch,
      details: { resultIds: ready.map((d) => d.id) }
    };
  }
  return {
    ...base,
    eligible: true,
    code: "RECLAIMABLE",
    reason: registeredHere || pathExistsOnDisk ? "Terminal Task lane is clean, settled, and unambiguously owned; safe to remove worktree registration and directory." : "Task lane registration is residual-only; safe idempotent cleanup.",
    worktree: resolvedWorktree,
    branch: expectedBranch,
    targetBranch: task.targetBranch,
    details: {
      pathExistsOnDisk,
      registered: registration.registered,
      expectedPath
    }
  };
}
async function reclaimTaskWorktree(input) {
  const diagnostic = await evaluateTaskWorktreeReclaim(input);
  if (input.preview) {
    return {
      ...diagnostic,
      reclaimed: false,
      alreadyGone: diagnostic.code === "ALREADY_GONE"
    };
  }
  if (!diagnostic.eligible) {
    return { ...diagnostic, reclaimed: false, alreadyGone: false };
  }
  if (diagnostic.code === "ALREADY_GONE") {
    return {
      ...diagnostic,
      code: "ALREADY_GONE",
      reclaimed: true,
      alreadyGone: true
    };
  }
  const workspaceRoot = nodePath4.resolve(input.workspaceRoot);
  const worktree = diagnostic.worktree;
  const expectedBranch = diagnostic.branch?.trim();
  if (!worktree) {
    return {
      ...diagnostic,
      eligible: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: "Reclaim eligible but worktree path missing from diagnostic.",
      reclaimed: false,
      alreadyGone: false
    };
  }
  if (!expectedBranch) {
    return {
      ...diagnostic,
      eligible: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: "Reclaim eligible but expected Task branch missing from diagnostic.",
      reclaimed: false,
      alreadyGone: false
    };
  }
  try {
    if (input.beforeRemoveForTests) {
      await input.beforeRemoveForTests();
    }
    const stillExists = await pathExists2(worktree);
    const registration = await inspectWorktreeRegistration(workspaceRoot, worktree);
    if (!stillExists && !registration.registered) {
      return {
        ...diagnostic,
        code: "ALREADY_GONE",
        reason: "Task worktree already removed before git worktree remove (idempotent).",
        reclaimed: true,
        alreadyGone: true
      };
    }
    const regCompare = compareRegistrationToExpected({
      registration,
      expectedPath: worktree,
      expectedBranch,
      requireRegistered: stillExists || registration.registered
    });
    if (!regCompare.ok) {
      return {
        ...diagnostic,
        eligible: false,
        code: regCompare.code,
        reason: regCompare.reason,
        reclaimed: false,
        alreadyGone: false,
        details: {
          ...diagnostic.details ?? {},
          ...regCompare.details ?? {}
        }
      };
    }
    const ownership = await revalidateExactWorktreeOwnership({
      workspaceRoot,
      expectedPath: worktree,
      expectedBranch,
      /** When dir is absent, skip cwd HEAD check; still require branch tip + registration. */
      requireOnDiskHead: stillExists
    });
    if (!ownership.ok) {
      return {
        ...diagnostic,
        eligible: false,
        code: ownership.code,
        reason: ownership.reason,
        reclaimed: false,
        alreadyGone: false,
        details: {
          ...diagnostic.details ?? {},
          ...ownership.details ?? {}
        }
      };
    }
    if (stillExists) {
      let dirty;
      try {
        dirty = await inspectWorktreeDirtiness(worktree);
      } catch (err) {
        return {
          ...diagnostic,
          eligible: false,
          code: "REMOVE_FAILED",
          reason: `Refusing remove: dirtiness re-check failed (${err instanceof Error ? err.message : String(err)}); retry later.`,
          reclaimed: false,
          alreadyGone: false
        };
      }
      if (dirty.dirty) {
        return {
          ...diagnostic,
          eligible: false,
          code: "DIRTY",
          reason: `Refusing remove: worktree became dirty before lane delete (${dirty.changeCount} change(s)).`,
          reclaimed: false,
          alreadyGone: false,
          details: {
            ...diagnostic.details ?? {},
            trackedDirty: dirty.trackedDirty,
            untrackedDirty: dirty.untrackedDirty,
            changeCount: dirty.changeCount,
            dirtySample: dirty.sample
          }
        };
      }
      const preReg = await inspectWorktreeRegistration(workspaceRoot, worktree);
      const preRegCompare = compareRegistrationToExpected({
        registration: preReg,
        expectedPath: worktree,
        expectedBranch,
        requireRegistered: true
      });
      if (!preRegCompare.ok) {
        return {
          ...diagnostic,
          eligible: false,
          code: preRegCompare.code,
          reason: `Refusing remove: registration mismatch immediately before lane delete \u2014 ${preRegCompare.reason}`,
          reclaimed: false,
          alreadyGone: false,
          details: {
            ...diagnostic.details ?? {},
            ...preRegCompare.details ?? {}
          }
        };
      }
      const preRemove = await revalidateExactWorktreeOwnership({
        workspaceRoot,
        expectedPath: worktree,
        expectedBranch,
        requireOnDiskHead: true
      });
      if (!preRemove.ok) {
        return {
          ...diagnostic,
          eligible: false,
          code: preRemove.code,
          reason: `Refusing remove: ownership revalidation failed immediately before lane delete \u2014 ${preRemove.reason}`,
          reclaimed: false,
          alreadyGone: false,
          details: {
            ...diagnostic.details ?? {},
            ...preRemove.details ?? {}
          }
        };
      }
      const preRemoveSession = await probeSessionSettledBeforeRemove(
        input,
        diagnostic
      );
      if (preRemoveSession) return preRemoveSession;
      const laneRm = await removeTaskLaneDirectorySafe(worktree, {
        rmLaneDirectoryForTests: input.rmLaneDirectoryForTests
      });
      if (!laneRm.ok) {
        return {
          ...diagnostic,
          eligible: false,
          code: "REMOVE_FAILED",
          reason: laneRm.reason,
          reclaimed: false,
          alreadyGone: false,
          details: {
            ...diagnostic.details ?? {},
            ...laneRm.details ?? {},
            linkSafety: "node-fs-rm-lane"
          }
        };
      }
      if (await pathExists2(worktree)) {
        return {
          ...diagnostic,
          eligible: false,
          code: "REMOVE_FAILED",
          reason: `Task lane directory still present after Node-safe remove at ${worktree}; leaving Git registration untouched.`,
          reclaimed: false,
          alreadyGone: false,
          details: {
            ...diagnostic.details ?? {},
            linkSafety: "node-fs-rm-lane"
          }
        };
      }
    }
    {
      const liveReg = await inspectWorktreeRegistration(workspaceRoot, worktree);
      if (liveReg.registered) {
        if (await pathExists2(worktree)) {
          return {
            ...diagnostic,
            eligible: false,
            code: "REMOVE_FAILED",
            reason: `Refusing metadata force cleanup: directory present at ${worktree}; Node-safe lane remove must clear it first.`,
            reclaimed: false,
            alreadyGone: false
          };
        }
        const preForce = await revalidateExactWorktreeOwnership({
          workspaceRoot,
          expectedPath: worktree,
          expectedBranch,
          requireOnDiskHead: false
        });
        if (!preForce.ok) {
          return {
            ...diagnostic,
            eligible: false,
            code: preForce.code,
            reason: `Refusing exact-path force metadata drop \u2014 ${preForce.reason}`,
            reclaimed: false,
            alreadyGone: false,
            details: {
              ...diagnostic.details ?? {},
              ...preForce.details ?? {}
            }
          };
        }
        const preForceSession = await probeSessionSettledBeforeRemove(
          input,
          diagnostic
        );
        if (preForceSession) return preForceSession;
        try {
          if (input.removeWorktreeMetadataForTests) {
            await input.removeWorktreeMetadataForTests(workspaceRoot, worktree);
          } else {
            await git2(workspaceRoot, ["worktree", "remove", "--force", worktree]);
          }
        } catch (err) {
          return {
            ...diagnostic,
            eligible: false,
            code: "REMOVE_FAILED",
            reason: `Failed to drop exact stale registration at ${worktree} (dir already absent): ${err instanceof Error ? err.message : String(err)}`,
            reclaimed: false,
            alreadyGone: false
          };
        }
        const afterMeta = await inspectWorktreeRegistration(workspaceRoot, worktree);
        if (afterMeta.registered || await pathExists2(worktree)) {
          return {
            ...diagnostic,
            eligible: false,
            code: "REMOVE_FAILED",
            reason: `Exact stale registration for ${worktree} remained after targeted metadata remove; refusing broader cleanup.`,
            reclaimed: false,
            alreadyGone: false
          };
        }
      }
    }
    const afterExists = await pathExists2(worktree);
    const afterReg = await inspectWorktreeRegistration(workspaceRoot, worktree);
    if (afterExists || afterReg.registered) {
      return {
        ...diagnostic,
        eligible: false,
        code: "REMOVE_FAILED",
        reason: `git worktree remove did not fully clear path/registration at ${worktree}`,
        reclaimed: false,
        alreadyGone: false,
        details: {
          ...diagnostic.details ?? {},
          afterExists,
          afterRegistered: afterReg.registered
        }
      };
    }
    return {
      ...diagnostic,
      code: "RECLAIMED",
      reason: `Removed Task worktree directory and Git registration at ${worktree}; branch and commits preserved.`,
      reclaimed: true,
      alreadyGone: false
    };
  } catch (err) {
    return {
      ...diagnostic,
      eligible: false,
      code: "REMOVE_FAILED",
      reason: `Failed to remove Task worktree: ${err instanceof Error ? err.message : String(err)}`,
      reclaimed: false,
      alreadyGone: false
    };
  }
}
async function reclaimTaskWorktreeForEnvelope(fs21, workspaceRoot, task, options = {}) {
  const taskId = task.id?.trim();
  const results = taskId ? await loadTaskResults(fs21, { taskId }) : [];
  return reclaimTaskWorktree({
    workspaceRoot,
    task,
    results,
    preview: options.preview,
    beforeRemoveForTests: options.beforeRemoveForTests,
    rmLaneDirectoryForTests: options.rmLaneDirectoryForTests,
    removeWorktreeMetadataForTests: options.removeWorktreeMetadataForTests,
    assertSessionSettledBeforeRemove: options.assertSessionSettledBeforeRemove
  });
}
async function probeSessionSettledBeforeRemove(input, diagnostic) {
  if (!input.assertSessionSettledBeforeRemove) return void 0;
  const settled = await input.assertSessionSettledBeforeRemove();
  if (settled.ok) return void 0;
  return {
    ...diagnostic,
    eligible: false,
    code: "SESSION_ACTIVE",
    reason: settled.reason,
    reclaimed: false,
    alreadyGone: false,
    details: {
      ...diagnostic.details ?? {},
      ...settled.details ?? {},
      settleProbe: "pre-remove"
    }
  };
}
async function evaluateTaskWorktreeReclaimForEnvelope(fs21, workspaceRoot, task) {
  const taskId = task.id?.trim();
  const results = taskId ? await loadTaskResults(fs21, { taskId }) : [];
  return evaluateTaskWorktreeReclaim({ workspaceRoot, task, results });
}
async function removeTaskLaneDirectorySafe(lanePath, options = {}) {
  const resolved = nodePath4.resolve(lanePath);
  let rootStat;
  try {
    rootStat = await nodeFs2.lstat(resolved);
  } catch (err) {
    return {
      ok: false,
      reason: `Cannot lstat Task lane root before Node-safe remove at ${resolved}: ${err instanceof Error ? err.message : String(err)}`,
      details: { lanePath: resolved }
    };
  }
  if (rootStat.isSymbolicLink()) {
    return {
      ok: false,
      reason: `Task lane root is a symlink/junction/reparse point at ${resolved}; refusing Node-safe remove (fail-closed, registration untouched).`,
      details: { lanePath: resolved, linkSafety: "lane-root-reparse" }
    };
  }
  if (!rootStat.isDirectory()) {
    return {
      ok: false,
      reason: `Task lane root is not a directory at ${resolved}; refusing Node-safe remove.`,
      details: { lanePath: resolved }
    };
  }
  try {
    if (options.rmLaneDirectoryForTests) {
      await options.rmLaneDirectoryForTests(resolved);
    } else {
      await nodeFs2.rm(resolved, { recursive: true, force: false });
    }
  } catch (err) {
    return {
      ok: false,
      reason: `Node-safe Task lane directory remove failed at ${resolved} (possible partial lane; Git registration left untouched; no prune/rollback): ${err instanceof Error ? err.message : String(err)}`,
      details: { lanePath: resolved, linkSafety: "node-fs-rm-lane" }
    };
  }
  return { ok: true };
}
async function evaluateAcceptedSettle(input) {
  const target = input.targetBranch.trim();
  if (!target) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: "Accepted Task is missing targetBranch; cannot verify integrate/settle."
    };
  }
  const related = input.results.filter((d) => d.taskId === input.taskId);
  const open5 = related.filter((d) => d.status === "ready");
  if (open5.length > 0) {
    return {
      ok: false,
      code: "UNINTEGRATED",
      reason: `Accepted Task still has ${open5.length} open result(ies); refuse reclaim.`,
      details: { resultIds: open5.map((d) => d.id) }
    };
  }
  const accepted = related.filter((d) => d.status === "accepted");
  const missingFromTaskResult = [];
  for (const d of accepted) {
    for (const ref of d.commits) {
      const sha = ref.trim();
      if (!sha) continue;
      const integrated = await findIntegratedCommit(input.workspaceRoot, sha, target);
      if (!integrated) missingFromTaskResult.push(sha);
    }
  }
  if (missingFromTaskResult.length > 0) {
    return {
      ok: false,
      code: "UNINTEGRATED",
      reason: `Accepted TaskResult commit(s) are not integrated into ${target}; refusing reclaim.`,
      details: {
        missingCommits: missingFromTaskResult.slice(0, 12),
        missingCount: missingFromTaskResult.length,
        targetBranch: target,
        source: "result"
      }
    };
  }
  const branchMissing = await listUnintegratedTaskBranchCommits({
    workspaceRoot: input.workspaceRoot,
    taskBranch: input.taskBranch,
    targetBranch: target,
    roleBranchBase: input.roleBranchBase
  });
  if (branchMissing.length > 0) {
    return {
      ok: false,
      code: "UNINTEGRATED",
      reason: `Task branch ${input.taskBranch} still has ${branchMissing.length} commit(s) not integrated into ${target} (including TaskResult-omitted tips); refusing reclaim.`,
      details: {
        missingCommits: branchMissing.slice(0, 12),
        missingCount: branchMissing.length,
        targetBranch: target,
        source: "task-branch"
      }
    };
  }
  return { ok: true };
}
async function listUnintegratedTaskBranchCommits(input) {
  const root = nodePath4.resolve(input.workspaceRoot);
  const branchRef = `refs/heads/${input.taskBranch}`;
  const branchExists = await gitOk2(root, ["show-ref", "--verify", "--quiet", branchRef]);
  if (!branchExists) {
    return [];
  }
  let range = `${input.targetBranch}..${branchRef}`;
  const base = input.roleBranchBase?.trim();
  if (base) {
    try {
      const fullBase = (await git2(root, ["rev-parse", base])).trim();
      if (fullBase && await gitOk2(root, ["merge-base", "--is-ancestor", fullBase, branchRef])) {
        range = `${fullBase}..${branchRef}`;
      }
    } catch {
    }
  }
  let output = "";
  try {
    output = await git2(root, ["log", range, "--format=%H"]);
  } catch {
    try {
      const tip = (await git2(root, ["rev-parse", branchRef])).trim();
      if (!tip) return [];
      const integrated = await findIntegratedCommit(root, tip, input.targetBranch);
      return integrated ? [] : [tip];
    } catch {
      return ["<unreadable-task-branch>"];
    }
  }
  const shas = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
  const missing = [];
  for (const sha of shas) {
    const integrated = await findIntegratedCommit(root, sha, input.targetBranch);
    if (!integrated) missing.push(sha);
  }
  return missing;
}
function compareRegistrationToExpected(input) {
  const expectedPath = nodePath4.resolve(input.expectedPath);
  const expectedBranch = input.expectedBranch.trim();
  const reg = input.registration;
  if (!reg.registered) {
    if (!input.requireRegistered) return { ok: true };
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Expected Git worktree registration missing at exact path ${expectedPath}; refusing remove.`,
      details: { expectedPath, expectedBranch }
    };
  }
  if (!reg.branch) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Worktree at ${expectedPath} is registered without a branch (detached/ambiguous); refusing remove.`,
      details: { expectedPath }
    };
  }
  if (reg.branch !== expectedBranch) {
    return {
      ok: false,
      code: "CONFLICTED_REGISTRATION",
      reason: `Fresh registration at ${expectedPath} is branch ${reg.branch}, expected diagnostic branch ${expectedBranch}.`,
      details: {
        registeredBranch: reg.branch,
        expectedBranch,
        registeredPath: reg.path,
        expectedPath
      }
    };
  }
  if (reg.path) {
    const regPath = nodePath4.resolve(reg.path);
    if (!isSameWorkspaceRoot(regPath, expectedPath)) {
      return {
        ok: false,
        code: "CONFLICTED_REGISTRATION",
        reason: `Fresh registration path ${regPath} does not match expected exact path ${expectedPath}.`,
        details: { registeredPath: regPath, expectedPath, expectedBranch }
      };
    }
  }
  return { ok: true };
}
async function revalidateExactWorktreeOwnership(input) {
  const root = nodePath4.resolve(input.workspaceRoot);
  const expectedPath = nodePath4.resolve(input.expectedPath);
  const expectedBranch = input.expectedBranch.trim();
  if (!expectedBranch.startsWith("tent-task/")) {
    return {
      ok: false,
      code: "NOT_APPLICABLE",
      reason: `Expected branch ${expectedBranch} is not a tent-task/* lane; refusing auto-GC ownership proof.`
    };
  }
  const registration = await inspectWorktreeRegistration(root, expectedPath);
  if (!registration.registered) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `No Git worktree registration at exact path ${expectedPath}; refusing remove.`,
      details: { expectedPath, expectedBranch }
    };
  }
  if (!registration.branch) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Worktree at ${expectedPath} is detached/unbranched; refusing remove.`,
      details: { expectedPath }
    };
  }
  if (registration.branch !== expectedBranch) {
    return {
      ok: false,
      code: "CONFLICTED_REGISTRATION",
      reason: `Registration at ${expectedPath} is branch ${registration.branch}, expected ${expectedBranch}.`,
      details: { registeredBranch: registration.branch, expectedBranch }
    };
  }
  const pathForBranch = await worktreePathForBranch(root, expectedBranch);
  if (!pathForBranch) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Branch ${expectedBranch} has no worktree registration; refusing remove.`,
      details: { expectedBranch }
    };
  }
  const branchPath = nodePath4.resolve(pathForBranch);
  let samePath = isSameWorkspaceRoot(branchPath, expectedPath);
  if (!samePath) {
    try {
      const [a, b] = await Promise.all([
        nodeFs2.realpath(branchPath).catch(() => branchPath),
        nodeFs2.realpath(expectedPath).catch(() => expectedPath)
      ]);
      samePath = isSameWorkspaceRoot(a, b);
    } catch {
      samePath = false;
    }
  }
  if (!samePath) {
    return {
      ok: false,
      code: "CONFLICTED_REGISTRATION",
      reason: `Branch ${expectedBranch} is registered at ${branchPath}, not exact path ${expectedPath}.`,
      details: { registeredPath: branchPath, expectedPath }
    };
  }
  let branchTip;
  try {
    branchTip = (await git2(root, ["rev-parse", `refs/heads/${expectedBranch}`])).trim();
  } catch (err) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Cannot resolve tip of ${expectedBranch}: ${err instanceof Error ? err.message : String(err)}`,
      details: { expectedBranch }
    };
  }
  if (!branchTip) {
    return {
      ok: false,
      code: "AMBIGUOUS_OWNERSHIP",
      reason: `Empty tip for ${expectedBranch}; refusing remove.`
    };
  }
  if (input.requireOnDiskHead) {
    if (!await pathExists2(expectedPath)) {
      return {
        ok: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `Expected on-disk worktree missing at ${expectedPath} during ownership proof.`
      };
    }
    let headTip;
    let currentBranch;
    try {
      headTip = (await git2(expectedPath, ["rev-parse", "HEAD"])).trim();
      currentBranch = (await git2(expectedPath, ["branch", "--show-current"])).trim();
    } catch (err) {
      return {
        ok: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `Cannot read HEAD/branch inside ${expectedPath}: ${err instanceof Error ? err.message : String(err)}`
      };
    }
    if (currentBranch !== expectedBranch) {
      return {
        ok: false,
        code: "CONFLICTED_REGISTRATION",
        reason: `On-disk worktree is on ${currentBranch || "(detached)"}, expected ${expectedBranch}.`,
        details: { currentBranch, expectedBranch, headTip, branchTip }
      };
    }
    if (headTip !== branchTip) {
      return {
        ok: false,
        code: "AMBIGUOUS_OWNERSHIP",
        reason: `On-disk HEAD ${headTip} does not match refs/heads/${expectedBranch} tip ${branchTip}.`,
        details: { headTip, branchTip, expectedBranch }
      };
    }
  }
  return {
    ok: true,
    registration: { ...registration, branchTip },
    branchTip
  };
}
async function inspectWorktreeRegistration(workspaceRoot, worktreePath) {
  const root = nodePath4.resolve(workspaceRoot);
  const target = nodePath4.resolve(worktreePath);
  const output = await git2(root, ["worktree", "list", "--porcelain"]).catch(() => "");
  let currentPath = "";
  let currentBranch;
  const entries = [];
  const flush = () => {
    if (!currentPath) return;
    entries.push({
      registered: true,
      path: currentPath,
      branch: currentBranch
    });
    currentPath = "";
    currentBranch = void 0;
  };
  for (const line of output.split(/\r?\n/)) {
    if (line.startsWith("worktree ")) {
      flush();
      currentPath = line.slice("worktree ".length);
      continue;
    }
    if (line.startsWith("branch refs/heads/")) {
      currentBranch = line.slice("branch refs/heads/".length);
    }
    if (line === "detached") {
      currentBranch = void 0;
    }
  }
  flush();
  for (const entry of entries) {
    if (!entry.path) continue;
    const candidate = nodePath4.resolve(entry.path);
    if (isSameWorkspaceRoot(candidate, target)) {
      return { registered: true, branch: entry.branch, path: candidate };
    }
    try {
      const [realCandidate, realTarget] = await Promise.all([
        nodeFs2.realpath(candidate).catch(() => candidate),
        nodeFs2.realpath(target).catch(() => target)
      ]);
      if (isSameWorkspaceRoot(realCandidate, realTarget)) {
        return { registered: true, branch: entry.branch, path: realCandidate };
      }
    } catch {
    }
  }
  return { registered: false };
}
async function worktreePathForBranch(workspaceRoot, branch) {
  const output = await git2(nodePath4.resolve(workspaceRoot), [
    "worktree",
    "list",
    "--porcelain"
  ]).catch(() => "");
  let currentPath = "";
  for (const line of output.split(/\r?\n/)) {
    if (line.startsWith("worktree ")) currentPath = line.slice("worktree ".length);
    if (line === `branch refs/heads/${branch}`) return currentPath;
  }
  return void 0;
}
async function pathExists2(path23) {
  try {
    await nodeFs2.access(path23);
    return true;
  } catch {
    return false;
  }
}
async function gitOk2(cwd, args) {
  try {
    await git2(cwd, args);
    return true;
  } catch {
    return false;
  }
}
function git2(cwd, args) {
  return new Promise((resolve15, reject) => {
    const child = spawn2("git", args, { cwd, windowsHide: true });
    let out = "";
    let err = "";
    child.stdout.on("data", (data) => out += data);
    child.stderr.on("data", (data) => err += data);
    child.on("close", (code) => {
      if (code === 0) resolve15(out);
      else reject(new Error(err.trim() || `git ${args.join(" ")} exit ${code}`));
    });
    child.on("error", reject);
  });
}

// src/core/workspace-settings.ts
var DEFAULT_SETTINGS = {
  defaultAcceptMode: DEFAULT_ACCEPT_MODE
};
function isAcceptModeValue(value) {
  return isAcceptMode(value);
}
function normalizeWorkspaceSettings(value) {
  if (!isRecord5(value)) {
    throw new WorkspaceSettingsError(
      "INVALID_PATCH",
      "Workspace settings must be an object"
    );
  }
  if ("defaultTaskResultPolicy" in value || "resultPolicy" in value) {
    throw new WorkspaceSettingsError(
      "INVALID_ACCEPT_MODE",
      "Workspace settings contain a retired acceptance-policy field; use defaultAcceptMode"
    );
  }
  const out = { ...value };
  if (out.defaultAcceptMode === void 0) {
    out.defaultAcceptMode = DEFAULT_ACCEPT_MODE;
  } else if (!isAcceptMode(out.defaultAcceptMode)) {
    throw new WorkspaceSettingsError(
      "INVALID_ACCEPT_MODE",
      `Invalid defaultAcceptMode: ${String(out.defaultAcceptMode)}`
    );
  }
  return out;
}
function defaultWorkspaceSettings() {
  return { ...DEFAULT_SETTINGS };
}
async function loadWorkspaceSettings(fs21) {
  if (!await fs21.exists(WORKSPACE_SETTINGS_PATH)) {
    return defaultWorkspaceSettings();
  }
  let parsed;
  try {
    parsed = JSON.parse(await fs21.readFile(WORKSPACE_SETTINGS_PATH));
  } catch {
    const backupPath = await backupCorruptRegistry(fs21, WORKSPACE_SETTINGS_PATH);
    const reset = defaultWorkspaceSettings();
    await writeSettingsUnlocked(fs21, reset);
    warnRegistryRecovered(
      WORKSPACE_SETTINGS_PATH,
      backupPath,
      "reset",
      "IMPORTANT: workspace settings cannot be inferred; restore needed keys from the backup."
    );
    return reset;
  }
  return normalizeWorkspaceSettings(parsed);
}
async function updateWorkspaceSettings(fs21, patch) {
  return withTentMutation(fs21, async () => {
    if (!isRecord5(patch)) {
      throw new WorkspaceSettingsError(
        "INVALID_PATCH",
        "workspace.settings.update patch must be an object"
      );
    }
    const before = await loadWorkspaceSettings(fs21);
    const nextRaw = { ...before };
    for (const [key2, value] of Object.entries(patch)) {
      if (key2 === "defaultAcceptMode") {
        if (value === void 0) continue;
        if (!isAcceptModeValue(value)) {
          throw new WorkspaceSettingsError(
            "INVALID_ACCEPT_MODE",
            `Invalid defaultAcceptMode: ${String(value)}`
          );
        }
        nextRaw.defaultAcceptMode = value;
        continue;
      }
      if (value === void 0) continue;
      nextRaw[key2] = value;
    }
    const next = normalizeWorkspaceSettings(nextRaw);
    const changed = !settingsEqual(before, next);
    if (changed) {
      await writeSettingsUnlocked(fs21, next);
    }
    return { settings: next, changed };
  });
}
var WorkspaceSettingsError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "WorkspaceSettingsError";
  }
};
async function writeSettingsUnlocked(fs21, settings) {
  const known = ["defaultAcceptMode"];
  const ordered = {};
  for (const key2 of known) {
    if (key2 in settings) ordered[key2] = settings[key2];
  }
  const rest = Object.keys(settings).filter((k) => !known.includes(k)).sort((a, b) => a.localeCompare(b));
  for (const key2 of rest) {
    ordered[key2] = settings[key2];
  }
  await fs21.writeFile(WORKSPACE_SETTINGS_PATH, JSON.stringify(ordered, null, 2) + "\n");
}
function settingsEqual(a, b) {
  return stableStringify(a) === stableStringify(b);
}
function stableStringify(value) {
  return JSON.stringify(sortKeysDeep(value));
}
function sortKeysDeep(value) {
  if (Array.isArray(value)) return value.map(sortKeysDeep);
  if (!isRecord5(value)) return value;
  const out = {};
  for (const key2 of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
    out[key2] = sortKeysDeep(value[key2]);
  }
  return out;
}
function isRecord5(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

// src/core/annotation.ts
var ANNOTATION_ID_PREFIX = "an-";
var AN_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
var AnnotationError = class extends Error {
  constructor(code, message2) {
    super(message2);
    this.code = code;
    this.name = "AnnotationError";
  }
};
var ANNOTATION_STATUSES = /* @__PURE__ */ new Set(["open", "resolved"]);
function isRecord6(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isRequiredString(value) {
  return typeof value === "string" && value.length > 0;
}
function isNonNegativeInt(value) {
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
}
function isValidDate(value) {
  return Number.isFinite(Date.parse(value));
}
function cloneRecord(item) {
  return { ...item };
}
function defaultClock() {
  return { now: () => (/* @__PURE__ */ new Date()).toISOString() };
}
function makeAnnotationId(rand = Math.random, len = 8) {
  let s = "";
  for (let i = 0; i < len; i++) {
    s += AN_ALPHABET[Math.floor(rand() * AN_ALPHABET.length)];
  }
  return ANNOTATION_ID_PREFIX + s;
}
function makeUniqueAnnotationId(existing, rand = Math.random) {
  for (let attempt = 0; attempt < 50; attempt++) {
    const id = makeAnnotationId(rand);
    if (!existing.has(id)) return id;
  }
  return makeAnnotationId(rand, 12);
}
function isAnnotationId(id) {
  return id.startsWith(ANNOTATION_ID_PREFIX) && id.length > ANNOTATION_ID_PREFIX.length;
}
function validateAnnotationAnchor(documentBody, quote, start, end) {
  if (typeof quote !== "string" || quote.length === 0) {
    throw new AnnotationError("INVALID_INPUT", "quote must be a non-empty string");
  }
  if (!Number.isInteger(start) || !Number.isInteger(end)) {
    throw new AnnotationError("RANGE", "start/end must be integers");
  }
  if (start < 0 || end < 0 || start > end) {
    throw new AnnotationError("RANGE", "start/end out of order or negative");
  }
  if (end > documentBody.length) {
    throw new AnnotationError("RANGE", "annotation range exceeds document body");
  }
  const slice = documentBody.slice(start, end);
  if (slice !== quote) {
    throw new AnnotationError(
      "QUOTE_MISMATCH",
      "quote does not match document body at the given range"
    );
  }
}
function findQuoteOccurrences(body, quote) {
  if (!quote) return [];
  const hits = [];
  let from = 0;
  while (from <= body.length - quote.length) {
    const idx = body.indexOf(quote, from);
    if (idx === -1) break;
    hits.push(idx);
    from = idx + 1;
  }
  return hits;
}
function projectAnnotation(record, documentBody) {
  const base = cloneRecord(record);
  if (documentBody === null) {
    return {
      ...base,
      anchorState: "orphan",
      orphanReason: "missing-node"
    };
  }
  const { quote, start, end } = record;
  if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && end >= start && end <= documentBody.length && documentBody.slice(start, end) === quote) {
    return {
      ...base,
      anchorState: "anchored",
      currentStart: start,
      currentEnd: end
    };
  }
  const hits = findQuoteOccurrences(documentBody, quote);
  if (hits.length === 0) {
    return {
      ...base,
      anchorState: "orphan",
      orphanReason: "quote-mismatch"
    };
  }
  if (hits.length === 1) {
    const currentStart2 = hits[0];
    return {
      ...base,
      anchorState: "relocated",
      currentStart: currentStart2,
      currentEnd: currentStart2 + quote.length
    };
  }
  let bestDist = Number.POSITIVE_INFINITY;
  let bestStarts = [];
  for (const hit of hits) {
    const dist = Math.abs(hit - start);
    if (dist < bestDist) {
      bestDist = dist;
      bestStarts = [hit];
    } else if (dist === bestDist) {
      bestStarts.push(hit);
    }
  }
  if (bestStarts.length !== 1) {
    return {
      ...base,
      anchorState: "orphan",
      orphanReason: "ambiguous"
    };
  }
  const currentStart = bestStarts[0];
  return {
    ...base,
    anchorState: "relocated",
    currentStart,
    currentEnd: currentStart + quote.length
  };
}
function parseAnnotation(value) {
  if (!isRecord6(value)) return null;
  const {
    id,
    nodeId,
    quote,
    start,
    end,
    documentEtag,
    body,
    author,
    status,
    createdAt,
    updatedAt,
    resolvedAt
  } = value;
  if (!isRequiredString(id) || !isAnnotationId(id) || !isRequiredString(nodeId) || !isRequiredString(quote) || !isNonNegativeInt(start) || !isNonNegativeInt(end) || end < start || !isRequiredString(documentEtag) || typeof body !== "string" || body.length === 0 || author !== "user" || typeof status !== "string" || !ANNOTATION_STATUSES.has(status) || !isRequiredString(createdAt) || !isRequiredString(updatedAt) || !isValidDate(createdAt) || !isValidDate(updatedAt) || resolvedAt !== void 0 && (typeof resolvedAt !== "string" || !isValidDate(resolvedAt))) {
    return null;
  }
  return {
    id,
    nodeId,
    quote,
    start,
    end,
    documentEtag,
    body,
    author: "user",
    status,
    createdAt,
    updatedAt,
    ...resolvedAt !== void 0 ? { resolvedAt } : {}
  };
}
function parseAnnotationFile(value) {
  if (!isRecord6(value) || !Array.isArray(value.annotations)) return null;
  const annotations = [];
  for (const item of value.annotations) {
    const parsed = parseAnnotation(item);
    if (!parsed) return null;
    annotations.push(parsed);
  }
  return { annotations };
}
function emptyFile() {
  return { annotations: [] };
}
async function writeFileUnlocked(fs21, file) {
  const ordered = {
    annotations: file.annotations.map((a) => {
      const row = {
        id: a.id,
        nodeId: a.nodeId,
        quote: a.quote,
        start: a.start,
        end: a.end,
        documentEtag: a.documentEtag,
        body: a.body,
        author: a.author,
        status: a.status,
        createdAt: a.createdAt,
        updatedAt: a.updatedAt
      };
      if (a.resolvedAt !== void 0) row.resolvedAt = a.resolvedAt;
      return row;
    })
  };
  await fs21.writeFile(ANNOTATIONS_PATH, JSON.stringify(ordered, null, 2) + "\n");
}
async function loadAnnotations(fs21) {
  if (!await fs21.exists(ANNOTATIONS_PATH)) {
    return emptyFile();
  }
  try {
    const parsed = JSON.parse(await fs21.readFile(ANNOTATIONS_PATH));
    const file = parseAnnotationFile(parsed);
    if (!file) {
      throw new Error("invalid annotation file shape");
    }
    return {
      annotations: file.annotations.map(cloneRecord)
    };
  } catch {
    const backupPath = await backupCorruptRegistry(fs21, ANNOTATIONS_PATH);
    const reset = emptyFile();
    await writeFileUnlocked(fs21, reset);
    warnRegistryRecovered(
      ANNOTATIONS_PATH,
      backupPath,
      "reset",
      "IMPORTANT: annotations cannot be inferred; restore from the backup if needed."
    );
    return reset;
  }
}
async function listAnnotationRecords(fs21, nodeId) {
  const file = await loadAnnotations(fs21);
  const rows = nodeId ? file.annotations.filter((a) => a.nodeId === nodeId) : file.annotations;
  return rows.map(cloneRecord);
}
async function createAnnotation(fs21, input, opts) {
  const clock = opts?.clock ?? defaultClock();
  const rand = opts?.rand ?? Math.random;
  if (!isRequiredString(input.nodeId)) {
    throw new AnnotationError("INVALID_INPUT", "nodeId is required");
  }
  if (!isRequiredString(input.documentEtag)) {
    throw new AnnotationError("INVALID_INPUT", "documentEtag is required");
  }
  if (typeof input.body !== "string" || input.body.trim().length === 0) {
    throw new AnnotationError("INVALID_INPUT", "body must be a non-empty string");
  }
  if (typeof input.quote !== "string" || input.quote.length === 0) {
    throw new AnnotationError("INVALID_INPUT", "quote must be a non-empty string");
  }
  validateAnnotationAnchor(input.documentBody, input.quote, input.start, input.end);
  return withTentMutation(fs21, async () => {
    const file = await loadAnnotations(fs21);
    const existing = new Set(file.annotations.map((a) => a.id));
    const now = clock.now();
    const record = {
      id: makeUniqueAnnotationId(existing, rand),
      nodeId: input.nodeId,
      quote: input.quote,
      start: input.start,
      end: input.end,
      documentEtag: input.documentEtag,
      body: input.body.trim(),
      author: "user",
      status: "open",
      createdAt: now,
      updatedAt: now
    };
    file.annotations.push(record);
    await writeFileUnlocked(fs21, file);
    return cloneRecord(record);
  });
}
async function resolveAnnotation(fs21, id, opts) {
  const clock = opts?.clock ?? defaultClock();
  return withTentMutation(fs21, async () => {
    const file = await loadAnnotations(fs21);
    const idx = file.annotations.findIndex((a) => a.id === id);
    if (idx < 0) {
      throw new AnnotationError("NOT_FOUND", `Annotation not found: ${id}`);
    }
    const current = file.annotations[idx];
    if (current.status === "resolved") {
      return cloneRecord(current);
    }
    const now = clock.now();
    const next = {
      ...current,
      status: "resolved",
      updatedAt: now,
      resolvedAt: now
    };
    file.annotations[idx] = next;
    await writeFileUnlocked(fs21, file);
    return cloneRecord(next);
  });
}
async function reopenAnnotation(fs21, id, opts) {
  const clock = opts?.clock ?? defaultClock();
  return withTentMutation(fs21, async () => {
    const file = await loadAnnotations(fs21);
    const idx = file.annotations.findIndex((a) => a.id === id);
    if (idx < 0) {
      throw new AnnotationError("NOT_FOUND", `Annotation not found: ${id}`);
    }
    const current = file.annotations[idx];
    if (current.status === "open") {
      const cleaned = {
        id: current.id,
        nodeId: current.nodeId,
        quote: current.quote,
        start: current.start,
        end: current.end,
        documentEtag: current.documentEtag,
        body: current.body,
        author: "user",
        status: "open",
        createdAt: current.createdAt,
        updatedAt: current.updatedAt
      };
      if (current.resolvedAt !== void 0) {
        file.annotations[idx] = cleaned;
        await writeFileUnlocked(fs21, file);
      }
      return cloneRecord(file.annotations[idx]);
    }
    const now = clock.now();
    const next = {
      id: current.id,
      nodeId: current.nodeId,
      quote: current.quote,
      start: current.start,
      end: current.end,
      documentEtag: current.documentEtag,
      body: current.body,
      author: "user",
      status: "open",
      createdAt: current.createdAt,
      updatedAt: now
    };
    file.annotations[idx] = next;
    await writeFileUnlocked(fs21, file);
    return cloneRecord(next);
  });
}
async function deleteAnnotation(fs21, id) {
  return withTentMutation(fs21, async () => {
    const file = await loadAnnotations(fs21);
    const idx = file.annotations.findIndex((a) => a.id === id);
    if (idx < 0) return null;
    const [removed] = file.annotations.splice(idx, 1);
    await writeFileUnlocked(fs21, file);
    return removed ? cloneRecord(removed) : null;
  });
}

// src/core/task-node-refs.ts
var MISSING_TASK_NODE_SELECTION = "MISSING_TASK_NODE_SELECTION: Task.nodeIds is required.";
function normalizedSelection(task) {
  const label = task.id || task.path || "(unknown)";
  try {
    return normalizeTaskNodeSelection({
      nodeIds: task.nodeIds
    });
  } catch (error) {
    const wrapped = new Error(`${MISSING_TASK_NODE_SELECTION} task=${label}`);
    wrapped.cause = error;
    throw wrapped;
  }
}
function taskReferencedNodeIds(task) {
  const selection = normalizedSelection(task);
  return [...selection.nodeIds];
}
function taskDirectlyReferencesNode(task, nodeId) {
  if (!nodeId) return false;
  return normalizedSelection(task).nodeIds.includes(nodeId);
}
function listDirectActiveTasksForNode(nodeId, tasks) {
  const matches = tasks.filter(
    (task) => isActiveTaskState(task.state) && taskDirectlyReferencesNode(task, nodeId)
  );
  return sortTasksDeterministically(matches);
}
function sortTasksDeterministically(tasks) {
  return [...tasks].sort((a, b) => {
    const ca = a.createdAt || "";
    const cb = b.createdAt || "";
    if (ca !== cb) return ca.localeCompare(cb);
    const ia = a.id || "";
    const ib = b.id || "";
    if (ia !== ib) return ia.localeCompare(ib);
    return (a.path || "").localeCompare(b.path || "");
  });
}

// src/runtime/types.ts
var ACP_PERMISSION_REQUEST_COUNT_MAX = 255;
var ACP_OBSERVATION_TEXT_BYTES = 256;
var ACP_OBSERVATION_SIGNAL_BYTES = 32;
var EXTERNAL_ADAPTER_ID = "external";
var SESSION_ID_PREFIX = "ss-";
var SESSION_ALPHABET = "0123456789abcdefghjkmnpqrstvwxyz";
function makeSessionId(rand = Math.random, len = 8) {
  let s = "";
  for (let i = 0; i < len; i++) {
    s += SESSION_ALPHABET[Math.floor(rand() * SESSION_ALPHABET.length)];
  }
  return SESSION_ID_PREFIX + s;
}
function isSessionId2(id) {
  return id.startsWith(SESSION_ID_PREFIX) && id.length > SESSION_ID_PREFIX.length;
}
function recordExternalKey(rec) {
  const key2 = rec.externalKey?.trim();
  return key2 || void 0;
}

// src/runtime/session-registry.ts
import * as fs5 from "node:fs/promises";
import * as path6 from "node:path";

// src/machine-state.ts
import * as fs4 from "node:fs/promises";
import * as path5 from "node:path";
function isNotFoundError(err) {
  return !!err && typeof err === "object" && "code" in err && err.code === "ENOENT";
}
function isRetryableRenameError2(err) {
  if (!err || typeof err !== "object" || !("code" in err)) return false;
  const code = err.code;
  return code === "EPERM" || code === "EBUSY" || code === "EACCES" || code === "EEXIST";
}
async function writeJsonAtomic(filePath, value) {
  await fs4.mkdir(path5.dirname(filePath), { recursive: true });
  const body = JSON.stringify(value, null, 2) + "\n";
  const tmp = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
  try {
    await fs4.writeFile(tmp, body, "utf8");
    await renameReplace2(tmp, filePath);
  } catch (err) {
    try {
      await fs4.unlink(tmp);
    } catch {
    }
    throw err;
  }
}
async function renameReplace2(tmp, filePath) {
  const attempts = process.platform === "win32" ? 8 : 1;
  let lastErr;
  for (let i = 0; i < attempts; i++) {
    try {
      await fs4.rename(tmp, filePath);
      return;
    } catch (err) {
      lastErr = err;
      if (!isRetryableRenameError2(err) || i === attempts - 1) break;
      await new Promise((r) => setTimeout(r, 5 + i * 5));
    }
  }
  throw lastErr;
}
async function backupCorruptMachineFile(filePath) {
  const backupPath = `${filePath}.corrupt-${corruptTimestamp()}`;
  try {
    await fs4.rename(filePath, backupPath);
    return backupPath;
  } catch {
    await fs4.copyFile(filePath, backupPath);
    try {
      await fs4.unlink(filePath);
    } catch {
    }
    return backupPath;
  }
}
function warnCorruptMachineState(filePath, backupPath, action, extra = "") {
  console.error(
    `WARNING: ${filePath} was corrupt; backed up to ${backupPath} and ${action}. Review it.${extra ? ` ${extra}` : ""}`
  );
}
function corruptTimestamp() {
  return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
}

// src/runtime/session-registry.ts
var SESSION_STATES = /* @__PURE__ */ new Set([
  "reserved",
  "starting",
  "live",
  "waiting-user",
  "stopped",
  "failed",
  "external"
]);
var STOP_REASONS = /* @__PURE__ */ new Set(["user", "interrupt", "shutdown"]);
function sessionsDir(dataDir) {
  return path6.join(dataDir, "sessions");
}
function sessionFilePath(dataDir, sessionId) {
  return path6.join(sessionsDir(dataDir), `${sessionId}.json`);
}
function assertSessionId(sessionId) {
  if (!isSessionId2(sessionId)) {
    throw new Error(`Invalid session id: ${sessionId}`);
  }
}
function isNonEmptyString(value) {
  return typeof value === "string" && value.length > 0;
}
function isPlainObject2(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function isSessionState(value) {
  return typeof value === "string" && SESSION_STATES.has(value);
}
function parseAcpRuntimeObservation(value) {
  if (!isPlainObject2(value)) return void 0;
  const allowed = /* @__PURE__ */ new Set([
    "permissionRequestCount",
    "permissionPolicy",
    "permissionDecision",
    "permissionOutcome",
    "promptStopReason",
    "spontaneousChildExit",
    "exitCode",
    "signal"
  ]);
  if (Object.keys(value).some((key2) => !allowed.has(key2))) return void 0;
  if (!Number.isInteger(value.permissionRequestCount) || value.permissionRequestCount < 0 || value.permissionRequestCount > ACP_PERMISSION_REQUEST_COUNT_MAX) return void 0;
  if (!(/* @__PURE__ */ new Set(["allow", "ask", "deny"])).has(value.permissionPolicy)) {
    return void 0;
  }
  if (value.permissionDecision !== void 0 && !(/* @__PURE__ */ new Set(["allow", "deny"])).has(value.permissionDecision)) return void 0;
  if (value.permissionOutcome !== void 0 && !(/* @__PURE__ */ new Set(["allow_once", "cancelled"])).has(value.permissionOutcome)) return void 0;
  if (typeof value.spontaneousChildExit !== "boolean") return void 0;
  if (value.promptStopReason !== void 0 && (typeof value.promptStopReason !== "string" || utf8Bytes(value.promptStopReason) > ACP_OBSERVATION_TEXT_BYTES)) return void 0;
  if (value.exitCode !== void 0 && value.exitCode !== null && (typeof value.exitCode !== "number" || !Number.isInteger(value.exitCode) || value.exitCode < -2147483648 || value.exitCode > 2147483647)) return void 0;
  if (value.signal !== void 0 && (typeof value.signal !== "string" || utf8Bytes(value.signal) > ACP_OBSERVATION_SIGNAL_BYTES)) return void 0;
  return {
    permissionRequestCount: value.permissionRequestCount,
    permissionPolicy: value.permissionPolicy,
    ...value.permissionDecision !== void 0 ? {
      permissionDecision: value.permissionDecision
    } : {},
    ...value.permissionOutcome !== void 0 ? {
      permissionOutcome: value.permissionOutcome
    } : {},
    ...value.promptStopReason !== void 0 ? { promptStopReason: value.promptStopReason } : {},
    spontaneousChildExit: value.spontaneousChildExit,
    ...value.exitCode !== void 0 ? { exitCode: value.exitCode } : {},
    ...value.signal !== void 0 ? { signal: value.signal } : {}
  };
}
function parseSessionRecord(data, sessionId) {
  if (!isPlainObject2(data)) return null;
  const allowedKeys = /* @__PURE__ */ new Set([
    "id",
    "connectionId",
    "adapterId",
    "connectionSnapshot",
    "acpSession",
    "acpObservation",
    "roleId",
    "state",
    "pid",
    "resumeToken",
    "runtimeWorkspace",
    "workspace",
    "workspaceLane",
    "createdAt",
    "updatedAt",
    "currentTaskId",
    "exitCode",
    "lastError",
    "stopReason",
    "providerContextRestored",
    "restoreReason",
    "replacedSessionId",
    "replacedBySessionId",
    "externalKey",
    "contextGeneration"
  ]);
  if (Object.keys(data).some((key2) => !allowedKeys.has(key2))) return null;
  if (!isNonEmptyString(data.id) || data.id !== sessionId) return null;
  if (!isSessionState(data.state)) return null;
  if (!isNonEmptyString(data.createdAt)) return null;
  if (!isNonEmptyString(data.updatedAt)) return null;
  const external = data.adapterId === EXTERNAL_ADAPTER_ID;
  if (external) {
    if (data.connectionId !== void 0 || data.connectionSnapshot !== void 0 || data.acpSession !== void 0 || data.acpObservation !== void 0) return null;
  } else {
    if (!isConnectionId(data.connectionId) || !isNonEmptyString(data.adapterId)) return null;
    if (data.roleId !== void 0) return null;
  }
  if ("pid" in data && data.pid !== void 0) {
    if (typeof data.pid !== "number" || !Number.isInteger(data.pid) || data.pid <= 0) {
      return null;
    }
  }
  if ("exitCode" in data && data.exitCode !== void 0) {
    if (data.exitCode !== null && (typeof data.exitCode !== "number" || !Number.isInteger(data.exitCode))) {
      return null;
    }
  }
  for (const key2 of [
    "roleId",
    "resumeToken",
    "workspace",
    "currentTaskId",
    "lastError",
    "externalKey",
    "contextGeneration"
  ]) {
    if (key2 in data && data[key2] !== void 0 && typeof data[key2] !== "string") {
      return null;
    }
  }
  if (data.roleId !== void 0 && (typeof data.roleId !== "string" || !isRoleId(data.roleId))) {
    return null;
  }
  if ("stopReason" in data && data.stopReason !== void 0) {
    if (typeof data.stopReason !== "string" || !STOP_REASONS.has(data.stopReason)) {
      return null;
    }
  }
  if ("providerContextRestored" in data && data.providerContextRestored !== void 0) {
    if (typeof data.providerContextRestored !== "boolean") return null;
  }
  for (const key2 of [
    "restoreReason",
    "replacedSessionId",
    "replacedBySessionId"
  ]) {
    if (key2 in data && data[key2] !== void 0 && typeof data[key2] !== "string") {
      return null;
    }
  }
  if ("runtimeWorkspace" in data && data.runtimeWorkspace !== void 0) {
    if (!isPlainObject2(data.runtimeWorkspace)) return null;
    if (!isNonEmptyString(data.runtimeWorkspace.cwd)) return null;
  }
  if ("workspaceLane" in data && data.workspaceLane !== void 0) {
    if (!isPlainObject2(data.workspaceLane)) return null;
    const lane = data.workspaceLane;
    if (!isNonEmptyString(lane.workspace)) return null;
    if (!isNonEmptyString(lane.worktree)) return null;
    if (!isNonEmptyString(lane.branch)) return null;
    if ("targetBranch" in lane && lane.targetBranch !== void 0 && typeof lane.targetBranch !== "string") {
      return null;
    }
  }
  if (external) return data;
  const snapshot = parseAgentConnectionSnapshot(data.connectionSnapshot);
  if (!snapshot) return null;
  if (snapshot.connectionId !== data.connectionId || !isConnectionId(snapshot.connectionId)) return null;
  if (!isNonEmptyString(snapshot.provider)) return null;
  if (snapshot.adapterId !== data.adapterId || !isNonEmptyString(snapshot.adapterId)) return null;
  if (!isNonEmptyString(snapshot.launchDigest)) return null;
  const acpSession = data.acpSession === void 0 ? void 0 : parseAcpSessionConfigSnapshot(data.acpSession);
  if (data.acpSession !== void 0 && !acpSession) return null;
  const acpObservation = data.acpObservation === void 0 ? void 0 : parseAcpRuntimeObservation(data.acpObservation);
  if (data.acpObservation !== void 0 && !acpObservation) return null;
  return {
    ...data,
    connectionSnapshot: snapshot,
    ...acpSession ? { acpSession } : {},
    ...acpObservation ? { acpObservation } : {}
  };
}
var SessionRegistry = class _SessionRegistry {
  constructor(dataDir) {
    this.dataDir = dataDir;
    /** Serialize disk mutations so stop + exit handlers cannot race rename. */
    this.writeChain = Promise.resolve();
  }
  get dataRoot() {
    return this.dataDir;
  }
  async ensureDir() {
    await fs5.mkdir(sessionsDir(this.dataDir), { recursive: true });
  }
  enqueue(fn) {
    const run = this.writeChain.then(fn, fn);
    this.writeChain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async create(record) {
    assertSessionId(record.id);
    return this.enqueue(async () => {
      await this.ensureDir();
      const file = sessionFilePath(this.dataDir, record.id);
      try {
        await fs5.access(file);
        throw new Error(`Session already exists: ${record.id}`);
      } catch (error) {
        if (!isNotFoundError(error)) throw error;
      }
      await writeJsonAtomic(file, record);
    });
  }
  /** Create-only persisted identity. Never overwrites an existing Session row. */
  async write(record) {
    return this.create(record);
  }
  async read(sessionId) {
    assertSessionId(sessionId);
    await this.writeChain;
    return this.readUnlocked(sessionId);
  }
  async update(sessionId, patch) {
    return this.enqueue(async () => {
      const current = await this.readUnlocked(sessionId);
      if (!current) throw new Error(`Session not found: ${sessionId}`);
      for (const immutable of ["id", "createdAt", "connectionId", "adapterId", "connectionSnapshot", "roleId"]) {
        if (Object.prototype.hasOwnProperty.call(patch, immutable)) {
          throw new Error(`SessionRegistry.update cannot mutate immutable field: ${immutable}`);
        }
      }
      const next = {
        ...current,
        ...patch,
        id: current.id,
        createdAt: current.createdAt,
        connectionId: current.connectionId,
        adapterId: current.adapterId,
        connectionSnapshot: current.connectionSnapshot,
        roleId: current.roleId,
        updatedAt: (/* @__PURE__ */ new Date()).toISOString()
      };
      await this.ensureDir();
      const file = sessionFilePath(this.dataDir, sessionId);
      await writeJsonAtomic(file, next);
      return next;
    });
  }
  async setState(sessionId, state, extra = {}) {
    return this.update(sessionId, { ...extra, state });
  }
  async list() {
    await this.ensureDir();
    const dir = sessionsDir(this.dataDir);
    let names;
    try {
      names = await fs5.readdir(dir);
    } catch (err) {
      if (isNotFoundError(err)) return [];
      throw err;
    }
    const out = [];
    for (const name of names) {
      if (!name.endsWith(".json")) continue;
      if (name.includes(".corrupt-") || name.endsWith(".tmp")) continue;
      const id = name.slice(0, -".json".length);
      if (!isSessionId2(id)) continue;
      const rec = await this.read(id);
      if (rec) out.push(rec);
    }
    out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
    return out;
  }
  async remove(sessionId) {
    assertSessionId(sessionId);
    return this.enqueue(async () => {
      try {
        await fs5.rm(sessionFilePath(this.dataDir, sessionId), { force: true });
      } catch {
      }
    });
  }
  /**
   * Non-terminal managed states that should be process-probed after service restart.
   * Does **not** include `external` (pull-host has no supervised PID).
   */
  static isNonTerminal(state) {
    return state === "starting" || state === "live" || state === "waiting-user";
  }
  /**
   * Session is still open for collaboration: managed non-terminal **or** pull-host external.
   * Use for list/status/idempotent enter; not for process reconcile (see isNonTerminal).
   */
  static isOpen(state) {
    return state === "reserved" || _SessionRegistry.isNonTerminal(state) || state === "external";
  }
  async readUnlocked(sessionId) {
    const file = sessionFilePath(this.dataDir, sessionId);
    try {
      const raw = await fs5.readFile(file, "utf8");
      let data;
      try {
        data = JSON.parse(raw);
      } catch {
        await this.quarantineCorrupt(file);
        return null;
      }
      const rec = parseSessionRecord(data, sessionId);
      if (!rec) {
        await this.quarantineCorrupt(file);
        return null;
      }
      return rec;
    } catch (err) {
      if (isNotFoundError(err)) return null;
      throw err;
    }
  }
  async quarantineCorrupt(file) {
    try {
      const backupPath = await backupCorruptMachineFile(file);
      warnCorruptMachineState(file, backupPath, "ignored");
    } catch {
    }
  }
};

// src/service/handlers.ts
import * as nodePath6 from "node:path";

// src/markdown/links.ts
var EXTERNAL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
var ARTIFACT_SCHEME_RE = /^(https?:|mailto:|tent-artifact:)/i;
function extractOutLinksDetailed(body) {
  const tree = fromMarkdown(body);
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  const definitions = collectDefinitions(tree);
  walk(tree, (node2) => {
    if (node2.type === "link") {
      const link3 = outLinkFromMdast(node2);
      if (link3) pushLink(out, seen, link3);
      return "skip";
    }
    if (node2.type === "linkReference") {
      const definition2 = definitions.get(normalizeIdentifier2(node2.identifier));
      const link3 = definition2 ? outLinkFromReference(node2, definition2) : null;
      if (link3) pushLink(out, seen, link3);
      return "skip";
    }
    if (node2.type === "image") return "skip";
    if (node2.type === "text") {
      const start = node2.position?.start?.offset;
      const end = node2.position?.end?.offset;
      const text3 = start != null && end != null ? body.slice(start, end) : node2.value;
      scanWikiInProse(text3, out, seen);
    }
  });
  return out;
}
function indexFromNodes(nodes) {
  return buildNodeIndex(nodes);
}
function resolveOutLink(index2, link3, fromNotePath) {
  if (link3.kind === "artifact") {
    return { raw: link3.raw, kind: "artifact", label: link3.label };
  }
  const meta = link3;
  const resolutionRaw = meta.targetPath ?? (link3.kind === "wiki" ? stripWikiSuffix(link3.raw).target : splitHref(link3.raw).pathPart);
  const target = normalizeTarget(resolutionRaw, fromNotePath);
  if (!isConceptCandidate(target) && !isConceptCandidate(resolutionRaw)) {
    return { raw: link3.raw, kind: "unresolved", label: link3.label };
  }
  const concept = resolveNode(index2, target) ?? resolveNode(index2, resolutionRaw);
  if (!concept) return { raw: link3.raw, kind: "unresolved", label: link3.label };
  return {
    raw: link3.raw,
    kind: link3.kind,
    targetNodeId: concept.id,
    targetPath: concept.path,
    label: link3.label ?? concept.name
  };
}
function buildBacklinkIndex(concepts) {
  const list2 = [...concepts];
  const index2 = /* @__PURE__ */ new Map();
  for (const c of list2) {
    const concept = {
      id: c.id,
      nodeId: c.id,
      path: c.path,
      notePath: c.notePath,
      name: c.name,
      type: "prompt"
    };
    add(index2, concept.id, concept);
    add(index2, concept.path, concept);
    add(index2, concept.notePath, concept);
    add(index2, concept.name, concept);
  }
  const reverse = /* @__PURE__ */ new Map();
  for (const c of list2) {
    for (const link3 of extractOutLinksDetailed(c.body)) {
      if (link3.kind === "artifact") continue;
      const resolved = resolveOutLink(index2, link3, c.notePath);
      if (!resolved.targetNodeId) continue;
      const arr = reverse.get(resolved.targetNodeId) ?? [];
      arr.push({
        fromNodeId: c.id,
        fromPath: c.path,
        fromName: c.name,
        raw: link3.raw,
        kind: link3.kind === "wiki" ? "wiki" : "md"
      });
      reverse.set(resolved.targetNodeId, arr);
    }
  }
  return reverse;
}
function walk(node2, visit) {
  if (visit(node2) === "skip") return;
  if ("children" in node2 && Array.isArray(node2.children)) {
    for (const child of node2.children) walk(child, visit);
  }
}
function outLinkFromMdast(node2) {
  return outLinkFromHref(node2.url, collectText(node2));
}
function outLinkFromReference(node2, definition2) {
  return outLinkFromHref(definition2.url, collectText(node2));
}
function outLinkFromHref(url, rawLabel) {
  const href = (url ?? "").trim();
  if (!href) return null;
  const label = rawLabel.replace(/\s+/g, " ").trim() || void 0;
  if (ARTIFACT_SCHEME_RE.test(href) || isExternalHref(href)) {
    return { raw: href, kind: "artifact", label, targetPath: href };
  }
  if (isPureAnchor(href)) return null;
  const { pathPart, fragment } = splitHref(href);
  if (!pathPart || isAttachmentPath(pathPart)) return null;
  return { raw: href, kind: "md", label, fragment, targetPath: pathPart };
}
function collectDefinitions(tree) {
  const definitions = /* @__PURE__ */ new Map();
  walk(tree, (node2) => {
    if (node2.type === "definition") {
      const key2 = normalizeIdentifier2(node2.identifier);
      if (!definitions.has(key2)) definitions.set(key2, node2);
    }
  });
  return definitions;
}
function normalizeIdentifier2(identifier) {
  return identifier.trim().replace(/\s+/g, " ").toLowerCase();
}
function collectText(node2) {
  if ("value" in node2 && typeof node2.value === "string") return node2.value;
  if ("children" in node2 && Array.isArray(node2.children)) {
    return node2.children.map(collectText).join("");
  }
  return "";
}
function scanWikiInProse(text3, out, seen) {
  let i = 0;
  while (i < text3.length) {
    if (text3[i] === "\\" && i + 1 < text3.length) {
      i += 2;
      continue;
    }
    if (text3[i] === "!" && text3[i + 1] === "[" && text3[i + 2] === "[") {
      const embedEnd = findWikiEnd(text3, i + 2);
      i = embedEnd === -1 ? i + 3 : embedEnd + 1;
      continue;
    }
    if (text3[i] === "[" && text3[i + 1] === "[") {
      const parsed = tryParseWikiLink(text3, i);
      if (parsed) {
        if (parsed.link) pushLink(out, seen, parsed.link);
        i = parsed.next;
        continue;
      }
    }
    i += 1;
  }
}
function tryParseWikiLink(text3, start) {
  if (text3[start] !== "[" || text3[start + 1] !== "[" || isEscaped(text3, start)) return null;
  const end = findWikiEnd(text3, start + 2);
  if (end === -1) return null;
  const next = end + 1;
  const inner = text3.slice(start + 2, end);
  if (!inner) return { link: null, next };
  let targetPart = inner;
  let label;
  const pipe = findUnescapedChar(inner, "|");
  if (pipe !== -1) {
    targetPart = inner.slice(0, pipe);
    label = inner.slice(pipe + 1).trim() || void 0;
  }
  const rawTarget = targetPart.trim();
  if (!rawTarget) return { link: null, next };
  const { target, fragment, blockRef } = stripWikiSuffix(rawTarget);
  if (!target || isAttachmentPath(target) || isPureAnchor(target) || isExternalHref(target)) {
    return { link: null, next };
  }
  return {
    link: { raw: rawTarget, kind: "wiki", label, fragment, blockRef, targetPath: target },
    next
  };
}
function findWikiEnd(text3, from) {
  for (let i = from; i < text3.length - 1; i++) {
    if (text3[i] === "]" && text3[i + 1] === "]" && !isEscaped(text3, i)) return i;
    if (text3[i] === "[" && text3[i + 1] === "[" && !isEscaped(text3, i)) return -1;
  }
  return -1;
}
function pushLink(out, seen, link3) {
  const key2 = `${link3.kind}:${link3.raw}|${link3.label ?? ""}`;
  if (seen.has(key2)) return;
  seen.add(key2);
  out.push(link3);
}
function stripWikiSuffix(raw) {
  const t = raw.trim();
  const caret = t.lastIndexOf("^");
  if (caret > 0) {
    const blockRef = t.slice(caret + 1).trim() || void 0;
    const before = t.slice(0, caret);
    const hash2 = before.indexOf("#");
    if (hash2 >= 0) {
      return {
        target: before.slice(0, hash2).trim(),
        fragment: before.slice(hash2 + 1).trim() || void 0,
        blockRef
      };
    }
    return { target: before.trim(), blockRef };
  }
  const hash = t.indexOf("#");
  if (hash >= 0) {
    return { target: t.slice(0, hash).trim(), fragment: t.slice(hash + 1).trim() || void 0 };
  }
  return { target: t };
}
function splitHref(href) {
  const t = href.trim();
  const q = t.indexOf("?");
  const h = t.indexOf("#");
  let pathEnd = t.length;
  if (q >= 0) pathEnd = Math.min(pathEnd, q);
  if (h >= 0) pathEnd = Math.min(pathEnd, h);
  return {
    pathPart: t.slice(0, pathEnd),
    fragment: h >= 0 ? t.slice(h + 1).split("?")[0] || void 0 : void 0
  };
}
function isConceptCandidate(target) {
  const t = target.trim();
  return Boolean(t) && !isPureAnchor(t) && !isExternalHref(t) && !isAttachmentPath(t);
}
function isPureAnchor(href) {
  const t = href.trim();
  return t.startsWith("#") || t === "";
}
function isExternalHref(href) {
  const t = href.trim();
  return t.startsWith("//") || ARTIFACT_SCHEME_RE.test(t) || EXTERNAL_SCHEME_RE.test(t);
}
function isAttachmentPath(href) {
  const t = href.trim().replace(/\\/g, "/").replace(/^\.\//, "");
  if (!t) return false;
  if (t === ATTACHMENTS_DIR || t.startsWith(`${ATTACHMENTS_DIR}/`)) return true;
  if (t.includes(`/${ATTACHMENTS_DIR}/`)) return true;
  const stack = [];
  for (const p of t.split("/")) {
    if (p === "." || p === "") continue;
    if (p === "..") stack.pop();
    else stack.push(p);
  }
  return stack[0] === ATTACHMENTS_DIR;
}
function isEscaped(text3, index2) {
  let bs = 0;
  for (let i = index2 - 1; i >= 0 && text3[i] === "\\"; i--) bs += 1;
  return bs % 2 === 1;
}
function findUnescapedChar(text3, ch) {
  for (let i = 0; i < text3.length; i++) {
    if (text3[i] === ch && !isEscaped(text3, i)) return i;
  }
  return -1;
}
function add(index2, key2, concept) {
  if (!key2) return;
  const list2 = index2.get(key2) ?? [];
  if (!list2.some((c) => c.id === concept.id)) list2.push(concept);
  index2.set(key2, list2);
  const all2 = index2.get("__all__") ?? [];
  if (!all2.some((c) => c.id === concept.id)) all2.push(concept);
  index2.set("__all__", all2);
}

// src/service/decision-request-store.ts
import * as fs7 from "node:fs/promises";
import * as path7 from "node:path";

// src/core/decision-request.ts
var REQUEST_FIELDS = [
  "id",
  "taskId",
  "requester",
  "target",
  "question",
  "options",
  "status"
];
function isRecord7(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function assertExactFields(value, fields, label) {
  const expected = new Set(fields);
  const actual = Object.keys(value);
  if (actual.length !== fields.length || actual.some((key2) => !expected.has(key2))) {
    throw new Error(`${label} has unexpected or missing fields.`);
  }
}
function requiredText(value, label) {
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error(`${label} must be a non-empty string.`);
  }
  return value;
}
function canonicalId(value, label, prefix, validate) {
  const id = requiredText(value, label);
  if (id !== id.trim() || id !== id.toLowerCase() || !validate(id)) {
    throw new Error(`${label} must be a canonical lowercase ${prefix}-* id.`);
  }
  return id;
}
function parseRequester(value) {
  const label = "Decision request.requester";
  if (!isRecord7(value)) throw new Error(`${label} must be a session actor object.`);
  assertExactFields(value, ["kind", "id"], label);
  if (value.kind !== "session") {
    throw new Error(`${label}.kind must be session.`);
  }
  return {
    kind: "session",
    id: canonicalId(value.id, `${label}.id`, "ss", isSessionId)
  };
}
function parseAuthorityActor(value, label) {
  if (!isRecord7(value)) throw new Error(`${label} must be an actor object.`);
  assertExactFields(value, ["kind", "id"], label);
  const kind = value.kind;
  const id = requiredText(value.id, `${label}.id`).trim();
  if (kind !== "user" && kind !== "role") {
    throw new Error(`${label}.kind must be user or role.`);
  }
  if (kind === "user" && id !== "user") {
    throw new Error(`${label}.id must be user for a user actor.`);
  }
  if (kind === "role" && id === "user") {
    throw new Error(`${label}.id must name a role, not user.`);
  }
  return { kind, id };
}
function assertSameAuthority(actual, expected, label) {
  if (actual.kind !== expected.kind || actual.id !== expected.id) {
    throw new Error(
      `${label} must exactly match target ${expected.kind}:${expected.id}; got ${actual.kind}:${actual.id}.`
    );
  }
}
function parseOption(value, index2) {
  if (!isRecord7(value)) throw new Error(`options[${index2}] must be an object.`);
  assertExactFields(value, ["id", "label"], `options[${index2}]`);
  return {
    id: requiredText(value.id, `options[${index2}].id`).trim(),
    label: requiredText(value.label, `options[${index2}].label`)
  };
}
function validateDecisionResponse(value, options) {
  if (!isRecord7(value)) throw new Error("response must be an object.");
  const kind = value.kind;
  if (kind === "option") {
    assertExactFields(value, ["kind", "optionId"], "response");
    const optionId = requiredText(value.optionId, "response.optionId").trim();
    if (!options.some((option) => option.id === optionId)) {
      throw new Error(`response.optionId is not one of the agent-provided options: ${optionId}.`);
    }
    return { kind, optionId };
  }
  if (kind === "custom") {
    assertExactFields(value, ["kind", "text"], "response");
    return { kind, text: requiredText(value.text, "response.text") };
  }
  if (kind === "deny") {
    assertExactFields(value, ["kind"], "response");
    return { kind };
  }
  throw new Error("response.kind must be option, custom, or deny.");
}
function validateDecisionRequest(value) {
  if (!isRecord7(value)) throw new Error("Decision request must be an object.");
  const status = value.status;
  const fields = status === "answered" ? [...REQUEST_FIELDS, "response", "resolvedBy"] : REQUEST_FIELDS;
  assertExactFields(value, fields, "Decision request");
  if (status !== "pending" && status !== "answered") {
    throw new Error("Decision request.status must be pending or answered.");
  }
  const optionsValue = value.options;
  if (!Array.isArray(optionsValue)) throw new Error("Decision request.options must be an array.");
  const options = optionsValue.map(parseOption);
  const optionIds = /* @__PURE__ */ new Set();
  for (const option of options) {
    if (optionIds.has(option.id)) throw new Error(`Duplicate decision option id: ${option.id}.`);
    optionIds.add(option.id);
  }
  const target = parseAuthorityActor(value.target, "Decision request.target");
  const base = {
    id: canonicalId(
      value.id,
      "Decision request.id",
      "dr",
      (id) => /^dr-[0-9a-z]{10}$/.test(id)
    ),
    taskId: canonicalId(value.taskId, "Decision request.taskId", "tk", isTaskId),
    requester: parseRequester(value.requester),
    target,
    question: requiredText(value.question, "Decision request.question"),
    options
  };
  if (status === "pending") {
    return { ...base, status };
  }
  const resolvedBy = parseAuthorityActor(value.resolvedBy, "Decision request.resolvedBy");
  assertSameAuthority(resolvedBy, target, "Decision request.resolvedBy");
  return {
    ...base,
    status,
    response: validateDecisionResponse(value.response, options),
    resolvedBy
  };
}
function answerDecisionRequest(request, responder, response) {
  const current = validateDecisionRequest(request);
  if (current.status !== "pending") {
    throw new Error(`Decision request already answered: ${current.id}.`);
  }
  const resolvedBy = parseAuthorityActor(responder, "Decision responder");
  assertSameAuthority(resolvedBy, current.target, "Decision responder");
  return {
    ...current,
    status: "answered",
    response: validateDecisionResponse(response, current.options),
    resolvedBy
  };
}
function escalateDecisionRequest(request) {
  const current = validateDecisionRequest(request);
  if (current.status !== "pending") {
    throw new Error("Only a pending decision request can be escalated.");
  }
  if (current.target.kind !== "role") {
    throw new Error("Only a role-targeted decision request can be escalated.");
  }
  return {
    ...current,
    target: { kind: "user", id: "user" }
  };
}

// src/service/authority-store-corruption.ts
import * as fs6 from "node:fs/promises";
var LATCH_SUFFIX = ".corrupt-latch.json";
var MAX_LATCH_BYTES = 4096;
var AuthorityStoreCorruptError = class extends Error {
  constructor(code, filePath, reason, backupPath) {
    super(`${code}: ${reason}`);
    this.code = code;
    this.filePath = filePath;
    this.reason = reason;
    this.backupPath = backupPath;
    this.name = "AuthorityStoreCorruptError";
  }
};
function latchPath(filePath) {
  return `${filePath}${LATCH_SUFFIX}`;
}
async function readBoundedUtf8(filePath) {
  const handle = await fs6.open(filePath, "r");
  try {
    const buffer = Buffer.alloc(MAX_LATCH_BYTES + 1);
    let offset = 0;
    while (offset < buffer.length) {
      const { bytesRead } = await handle.read(
        buffer,
        offset,
        buffer.length - offset,
        offset
      );
      if (bytesRead === 0) break;
      offset += bytesRead;
    }
    if (offset > MAX_LATCH_BYTES) {
      throw new Error("corruption latch exceeds byte bound");
    }
    return buffer.subarray(0, offset).toString("utf8");
  } finally {
    await handle.close();
  }
}
async function readAuthorityStoreCorruption(filePath, expectedCode) {
  const file = latchPath(filePath);
  let raw;
  try {
    raw = await readBoundedUtf8(file);
  } catch (error) {
    if (isNotFoundError(error)) return null;
    return new AuthorityStoreCorruptError(
      expectedCode,
      filePath,
      "persisted corruption latch is unreadable"
    );
  }
  try {
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
    const row = parsed;
    if (Object.keys(row).some((key2) => !["code", "reason", "createdAt"].includes(key2)) || row.code !== expectedCode || typeof row.reason !== "string" || !row.reason || Buffer.byteLength(row.reason, "utf8") > 256 || typeof row.createdAt !== "string" || !Number.isFinite(Date.parse(row.createdAt))) {
      throw new Error();
    }
    return new AuthorityStoreCorruptError(
      expectedCode,
      filePath,
      row.reason
    );
  } catch {
    return new AuthorityStoreCorruptError(
      expectedCode,
      filePath,
      "persisted corruption latch is invalid"
    );
  }
}
async function persistAuthorityStoreCorruption(filePath, code, reason, options) {
  const file = latchPath(filePath);
  try {
    await (options?.writeLatch ?? writeJsonAtomic)(file, {
      code,
      reason,
      createdAt: (/* @__PURE__ */ new Date()).toISOString()
    });
  } catch {
    return new AuthorityStoreCorruptError(
      code,
      filePath,
      `${reason}; corruption latch persistence failed`
    );
  }
  let backupPath;
  try {
    backupPath = await backupCorruptMachineFile(filePath);
    warnCorruptMachineState(filePath, backupPath, "ignored");
  } catch {
  }
  return new AuthorityStoreCorruptError(code, filePath, reason, backupPath);
}

// src/service/decision-request-store.ts
var DECISION_REQUEST_STORE_CORRUPT = "DECISION_REQUEST_STORE_CORRUPT";
var PENDING_FIELDS = /* @__PURE__ */ new Set([
  "id",
  "taskId",
  "requester",
  "target",
  "question",
  "options",
  "status",
  "workspaceId",
  "taskPath",
  "createdAt",
  "updatedAt"
]);
var ANSWERED_FIELDS = /* @__PURE__ */ new Set([
  ...PENDING_FIELDS,
  "response",
  "resolvedBy",
  "answeredAt"
]);
function isRecord8(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function requiredText2(value) {
  return typeof value === "string" && value.trim() ? value : null;
}
function validDate(value) {
  return typeof value === "string" && Number.isFinite(Date.parse(value));
}
function cloneRecord2(record) {
  return structuredClone(record);
}
function parseRecord(value) {
  if (!isRecord8(value)) return null;
  const expected = value.status === "answered" ? ANSWERED_FIELDS : PENDING_FIELDS;
  if (Object.keys(value).length !== expected.size || Object.keys(value).some((key2) => !expected.has(key2))) {
    return null;
  }
  const workspaceId = requiredText2(value.workspaceId);
  const taskPath = requiredText2(value.taskPath);
  if (!workspaceId || !taskPath || !validDate(value.createdAt) || !validDate(value.updatedAt)) {
    return null;
  }
  if (value.status === "answered" && !validDate(value.answeredAt)) return null;
  const coreValue = { ...value };
  delete coreValue.workspaceId;
  delete coreValue.taskPath;
  delete coreValue.createdAt;
  delete coreValue.updatedAt;
  delete coreValue.answeredAt;
  try {
    const request = validateDecisionRequest(coreValue);
    return {
      ...request,
      workspaceId,
      taskPath,
      createdAt: value.createdAt,
      updatedAt: value.updatedAt,
      ...request.status === "answered" ? { answeredAt: value.answeredAt } : {}
    };
  } catch {
    return null;
  }
}
function sameActor(left, right) {
  return left.kind === right.kind && left.id === right.id;
}
function sameResponse(left, right) {
  return JSON.stringify(left) === JSON.stringify(right);
}
function coreRequest(record) {
  const {
    workspaceId: _workspaceId,
    taskPath: _taskPath,
    createdAt: _createdAt,
    updatedAt: _updatedAt,
    answeredAt: _answeredAt,
    ...request
  } = record;
  return validateDecisionRequest(request);
}
var DecisionRequestStore = class {
  constructor(dataDir, options) {
    this.items = /* @__PURE__ */ new Map();
    this.loaded = false;
    this.corruptError = null;
    this.closed = false;
    this.shutdownPromise = null;
    this.chain = Promise.resolve();
    this.file = path7.join(dataDir, "decision-requests.json");
    this.writeState = options?.writeState ?? writeJsonAtomic;
  }
  enqueue(fn) {
    const run = this.chain.then(fn, fn);
    this.chain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async ensureLoaded() {
    this.throwIfCorrupt();
    if (this.loaded) return;
    await this.enqueue(async () => {
      this.throwIfCorrupt();
      if (this.loaded) return;
      const persistedCorruption = await readAuthorityStoreCorruption(
        this.file,
        DECISION_REQUEST_STORE_CORRUPT
      );
      if (persistedCorruption) {
        this.corruptError = persistedCorruption;
        this.items.clear();
        this.loaded = true;
        return;
      }
      try {
        const raw = await fs7.readFile(this.file, "utf8");
        const parsed = JSON.parse(raw);
        if (!isRecord8(parsed) || Object.keys(parsed).some((key2) => key2 !== "items")) {
          return this.latchCorrupt("root must contain only items");
        }
        if (!Array.isArray(parsed.items)) {
          return this.latchCorrupt("items must be an array");
        }
        const loaded = /* @__PURE__ */ new Map();
        for (const item of parsed.items) {
          const record = parseRecord(item);
          if (!record || loaded.has(record.id)) {
            return this.latchCorrupt("items contains an invalid or duplicate row");
          }
          loaded.set(record.id, record);
        }
        this.items = loaded;
        this.loaded = true;
      } catch (error) {
        if (isNotFoundError(error)) {
          this.loaded = true;
          return;
        }
        if (error instanceof SyntaxError) {
          return this.latchCorrupt("invalid JSON");
        }
        throw error;
      }
    });
  }
  async listPending(workspaceId) {
    await this.requireHealthy();
    return [...this.items.values()].filter(
      (item) => item.status === "pending" && (workspaceId === void 0 || item.workspaceId === workspaceId)
    ).map(cloneRecord2);
  }
  async getExact(workspaceId, taskPath, requestId) {
    await this.requireHealthy();
    const item = this.items.get(requestId);
    if (!item || item.workspaceId !== workspaceId || item.taskPath !== taskPath) return void 0;
    return cloneRecord2(item);
  }
  /** Public-id lookup; taskPath remains internal authority carried by the row. */
  async getExactById(workspaceId, requestId) {
    await this.requireHealthy();
    const item = this.items.get(requestId);
    if (!item || item.workspaceId !== workspaceId) return void 0;
    return cloneRecord2(item);
  }
  async getPendingForTask(workspaceId, taskPath) {
    await this.requireHealthy();
    const item = [...this.items.values()].find(
      (candidate) => candidate.status === "pending" && candidate.workspaceId === workspaceId && candidate.taskPath === taskPath
    );
    return item ? cloneRecord2(item) : void 0;
  }
  /**
   * Remove the one pending request owned by an exact Task before that Task
   * becomes terminal. Answered requests remain durable audit history.
   */
  async removePendingForTask(workspaceId, taskPath) {
    if (this.closed) throw new Error("DecisionRequest store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("DecisionRequest store is closed");
      const current = [...this.items.values()].find(
        (candidate) => candidate.status === "pending" && candidate.workspaceId === workspaceId && candidate.taskPath === taskPath
      );
      if (!current) return void 0;
      const next = new Map(this.items);
      next.delete(current.id);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneRecord2(current);
    });
  }
  async add(input) {
    if (this.closed) throw new Error("DecisionRequest store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("DecisionRequest store is closed");
      const request = validateDecisionRequest(input.request);
      if (request.status !== "pending") {
        throw new Error("DecisionRequestStore.add requires a pending request");
      }
      if (this.items.has(request.id)) {
        throw new Error(`Decision request already exists: ${request.id}`);
      }
      for (const item of this.items.values()) {
        if (item.status === "pending" && item.workspaceId === input.workspaceId && item.taskPath === input.taskPath) {
          throw new Error(`Task already has a pending decision request: ${item.id}`);
        }
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const record = {
        ...request,
        workspaceId: input.workspaceId,
        taskPath: input.taskPath,
        createdAt: now,
        updatedAt: now
      };
      const next = new Map(this.items);
      next.set(record.id, record);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneRecord2(record);
    });
  }
  async answerExact(input) {
    if (this.closed) throw new Error("DecisionRequest store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("DecisionRequest store is closed");
      const current = this.items.get(input.requestId);
      if (!current || current.workspaceId !== input.workspaceId || current.taskPath !== input.taskPath) {
        throw new Error(`Decision request not found for exact Task: ${input.requestId}`);
      }
      const response = validateDecisionResponse(input.response, current.options);
      if (current.status === "answered") {
        if (sameActor(current.resolvedBy, input.responder) && sameResponse(current.response, response)) {
          return cloneRecord2(current);
        }
        throw new Error(`Decision request already answered differently: ${current.id}`);
      }
      const answered = answerDecisionRequest(coreRequest(current), input.responder, response);
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const record = {
        ...answered,
        workspaceId: current.workspaceId,
        taskPath: current.taskPath,
        createdAt: current.createdAt,
        updatedAt: now,
        answeredAt: now
      };
      const next = new Map(this.items);
      next.set(record.id, record);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneRecord2(record);
    });
  }
  async escalateExact(workspaceId, taskPath, requestId) {
    if (this.closed) throw new Error("DecisionRequest store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("DecisionRequest store is closed");
      const current = this.items.get(requestId);
      if (!current || current.workspaceId !== workspaceId || current.taskPath !== taskPath) {
        throw new Error(`Decision request not found for exact Task: ${requestId}`);
      }
      if (current.status === "pending" && current.target.kind === "user") {
        return cloneRecord2(current);
      }
      const escalated = escalateDecisionRequest(coreRequest(current));
      const record = {
        ...escalated,
        workspaceId: current.workspaceId,
        taskPath: current.taskPath,
        createdAt: current.createdAt,
        updatedAt: (/* @__PURE__ */ new Date()).toISOString()
      };
      const next = new Map(this.items);
      next.set(record.id, record);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneRecord2(record);
    });
  }
  shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    this.closed = true;
    this.shutdownPromise = this.corruptError ? Promise.resolve() : this.ensureLoaded().then(
      () => void 0,
      (error) => {
        if (error === this.corruptError) return;
        throw error;
      }
    );
    return this.shutdownPromise;
  }
  async persistSnapshot(snapshot) {
    const items = [...snapshot.values()];
    const pending = items.filter((item) => item.status === "pending");
    const answered = items.filter((item) => item.status === "answered").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, 100);
    await this.writeState(this.file, { items: [...pending, ...answered] });
  }
  throwIfCorrupt() {
    if (this.corruptError) throw this.corruptError;
  }
  async requireHealthy() {
    await this.ensureLoaded();
    this.throwIfCorrupt();
  }
  async latchCorrupt(reason) {
    this.throwIfCorrupt();
    this.items.clear();
    this.loaded = true;
    this.corruptError = await persistAuthorityStoreCorruption(
      this.file,
      DECISION_REQUEST_STORE_CORRUPT,
      reason
    );
  }
};
function makeDecisionRequestId(rand = Math.random) {
  const alphabet = "0123456789abcdefghjkmnpqrstvwxyz";
  let value = "dr-";
  for (let index2 = 0; index2 < 10; index2 += 1) {
    value += alphabet[Math.floor(rand() * alphabet.length)];
  }
  return value;
}

// src/service/task-input-store.ts
import * as fs8 from "node:fs/promises";
import * as path8 from "node:path";
var TASK_INPUT_STORE_CORRUPT = "TASK_INPUT_STORE_CORRUPT";
function cloneInput(item) {
  return {
    ...item,
    ...item.contextRefs ? { contextRefs: [...item.contextRefs] } : {}
  };
}
var TASK_INPUT_STATUSES = /* @__PURE__ */ new Set([
  "pending",
  "processing",
  "delivered",
  "failed",
  "uncertain",
  "consumed",
  "cancelled"
]);
function isTaskInputOpenStatus(status) {
  return status === "pending" || status === "processing" || status === "failed";
}
function isTaskInputTaskResultBlockingStatus(status) {
  return isTaskInputOpenStatus(status) || status === "uncertain";
}
function isTaskInputCancelEligibleStatus(status) {
  return status === "pending" || status === "failed";
}
var TASK_INPUT_KINDS = /* @__PURE__ */ new Set([
  "user-input",
  "review-feedback",
  "decision-response"
]);
function normalizeTaskInputKind(kind) {
  if (kind === "review-feedback") return "review-feedback";
  if (kind === "decision-response") return "decision-response";
  return "user-input";
}
function isRecord9(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function isRequiredString2(value) {
  return typeof value === "string" && value.trim().length > 0;
}
function isOptionalString(value) {
  return value === void 0 || typeof value === "string";
}
function isValidDate2(value) {
  return Number.isFinite(Date.parse(value));
}
function parseInput(value) {
  if (!isRecord9(value)) return null;
  const {
    id,
    workspaceId,
    taskPath,
    taskId,
    sessionId,
    role,
    kind,
    text: text3,
    contextRefs,
    status,
    createdAt,
    updatedAt,
    deliveredAt,
    consumedAt,
    cancelledAt,
    lastError,
    failedAt,
    uncertainAt,
    resolvedBy
  } = value;
  if (!isRequiredString2(id) || !isRequiredString2(workspaceId) || !isRequiredString2(taskPath) || !isRequiredString2(createdAt) || !isRequiredString2(updatedAt) || !isValidDate2(createdAt) || !isValidDate2(updatedAt) || typeof status !== "string" || !TASK_INPUT_STATUSES.has(status) || !isOptionalString(taskId) || !isOptionalString(sessionId) || !isOptionalString(role) || !isOptionalString(text3) || !isOptionalString(deliveredAt) || !isOptionalString(consumedAt) || !isOptionalString(cancelledAt) || !isOptionalString(lastError) || !isOptionalString(failedAt) || !isOptionalString(uncertainAt) || !isOptionalString(resolvedBy) || deliveredAt !== void 0 && !isValidDate2(deliveredAt) || consumedAt !== void 0 && !isValidDate2(consumedAt) || cancelledAt !== void 0 && !isValidDate2(cancelledAt) || failedAt !== void 0 && !isValidDate2(failedAt) || uncertainAt !== void 0 && !isValidDate2(uncertainAt)) {
    return null;
  }
  let parsedKind;
  if (kind !== void 0) {
    if (typeof kind !== "string" || !TASK_INPUT_KINDS.has(kind)) {
      return null;
    }
    parsedKind = kind;
  }
  let parsedRefs;
  if (contextRefs !== void 0) {
    if (!Array.isArray(contextRefs)) return null;
    parsedRefs = [];
    for (const r of contextRefs) {
      if (!isRequiredString2(r)) return null;
      parsedRefs.push(r.trim());
    }
  }
  const resolvedKind = normalizeTaskInputKind(parsedKind);
  const hasText = typeof text3 === "string" && (resolvedKind === "review-feedback" || text3.trim().length > 0);
  const hasRefs = (parsedRefs?.length ?? 0) > 0;
  if (resolvedKind === "user-input" && !hasText && !hasRefs || resolvedKind === "decision-response" && !hasText) return null;
  if (resolvedKind === "review-feedback" && text3 !== void 0 && typeof text3 !== "string") {
    return null;
  }
  const persistedStatus = status;
  const restoredUncertain = persistedStatus === "processing";
  return {
    id,
    workspaceId,
    taskPath,
    ...taskId !== void 0 ? { taskId } : {},
    ...sessionId !== void 0 ? { sessionId } : {},
    ...role !== void 0 ? { role } : {},
    ...parsedKind !== void 0 ? { kind: parsedKind } : {},
    // review-feedback: preserve note exactly (no trim); user-input trims.
    ...typeof text3 === "string" ? {
      text: resolvedKind === "review-feedback" ? text3 : text3.trim()
    } : {},
    ...parsedRefs !== void 0 && parsedRefs.length > 0 ? { contextRefs: parsedRefs } : {},
    // A crashed processing turn has an unknowable provider boundary. Preserve
    // at-most-once semantics by requiring review instead of silently re-injecting.
    status: restoredUncertain ? "uncertain" : persistedStatus,
    createdAt,
    updatedAt,
    ...deliveredAt !== void 0 ? { deliveredAt } : {},
    ...consumedAt !== void 0 ? { consumedAt } : {},
    ...cancelledAt !== void 0 ? { cancelledAt } : {},
    ...typeof lastError === "string" ? { lastError } : restoredUncertain ? { lastError: "service restarted while managed inject was processing" } : {},
    ...failedAt !== void 0 ? { failedAt } : {},
    ...uncertainAt !== void 0 ? { uncertainAt } : restoredUncertain ? { uncertainAt: updatedAt } : {},
    ...resolvedBy !== void 0 ? { resolvedBy } : {}
  };
}
var TaskInputStore = class {
  constructor(dataDir, options) {
    this.items = /* @__PURE__ */ new Map();
    this.loaded = false;
    this.corruptError = null;
    this.closed = false;
    this.shutdownPromise = null;
    this.chain = Promise.resolve();
    /** Test-only: next persistSnapshot fails once, then clears. */
    this.nextPersistErrorForTests = null;
    this.file = path8.join(dataDir, "task-inputs.json");
    this.writeState = options?.writeState ?? writeJsonAtomic;
  }
  enqueue(fn) {
    const run = this.chain.then(fn, fn);
    this.chain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async ensureLoaded() {
    this.throwIfCorrupt();
    if (this.loaded) return;
    return this.enqueue(async () => {
      this.throwIfCorrupt();
      if (this.loaded) return;
      const persistedCorruption = await readAuthorityStoreCorruption(
        this.file,
        TASK_INPUT_STORE_CORRUPT
      );
      if (persistedCorruption) {
        this.corruptError = persistedCorruption;
        this.items.clear();
        this.loaded = true;
        return;
      }
      try {
        const raw = await fs8.readFile(this.file, "utf8");
        let parsed;
        try {
          parsed = JSON.parse(raw);
        } catch {
          return this.latchCorrupt("invalid JSON");
        }
        if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
          return this.latchCorrupt("root must be an object");
        }
        const items = parsed.items;
        if (items !== void 0 && !Array.isArray(items)) {
          return this.latchCorrupt("items must be an array");
        }
        const loaded = /* @__PURE__ */ new Map();
        for (const item of items ?? []) {
          const restored = parseInput(item);
          if (!restored || loaded.has(restored.id)) {
            return this.latchCorrupt("items contains an invalid or duplicate row");
          }
          loaded.set(restored.id, restored);
        }
        this.items = loaded;
        this.loaded = true;
      } catch (err) {
        if (isNotFoundError(err)) {
          this.loaded = true;
          return;
        }
        throw err;
      }
    });
  }
  /**
   * Retry/inject source (pending + failed; never processing or uncertain).
   * Always scoped by workspaceId + taskPath — no machine-global inbox.
   */
  async listRetryableForTask(workspaceId, taskPath) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "TaskInput.listRetryableForTask requires workspaceId and taskPath (no global inbox)"
      );
    }
    await this.requireHealthy();
    return [...this.items.values()].filter(
      (i) => (i.status === "pending" || i.status === "failed") && i.workspaceId === workspaceId && i.taskPath === taskPath
    ).map(cloneInput);
  }
  /**
   * Exact-task attention rows for humans/parents: retryable pending|failed plus
   * at-most-once uncertain. Never use this as a provider inject source.
   */
  async listAttentionForTask(workspaceId, taskPath) {
    const all2 = await this.listForTask(workspaceId, taskPath);
    return all2.filter(
      (item) => item.status === "pending" || item.status === "failed" || item.status === "uncertain"
    );
  }
  /**
   * All TaskInput rows for one (workspaceId, taskPath), any status.
   * Used by TaskResult gate authority (not a global inbox).
   */
  async listForTask(workspaceId, taskPath) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "TaskInput.listForTask requires workspaceId and taskPath (no global inbox)"
      );
    }
    await this.requireHealthy();
    return [...this.items.values()].filter(
      (i) => i.workspaceId === workspaceId && i.taskPath === taskPath
    ).map(cloneInput).sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
  }
  /**
   * Rows that must block a ready TaskResult for this task
   * (pending/processing/failed/uncertain).
   */
  async listBlockingForSubmit(workspaceId, taskPath) {
    const all2 = await this.listForTask(workspaceId, taskPath);
    return all2.filter((i) => isTaskInputTaskResultBlockingStatus(i.status));
  }
  /**
   * Scoped get: id alone is insufficient; workspaceId+taskPath must match.
   * Cross-workspace or wrong-task lookups return undefined (no leak).
   */
  async get(id, workspaceId, taskPath) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "TaskInput.get requires workspaceId and taskPath (no id-only lookup)"
      );
    }
    await this.requireHealthy();
    const item = this.items.get(id);
    if (!item) return void 0;
    if (item.workspaceId !== workspaceId || item.taskPath !== taskPath) {
      return void 0;
    }
    return cloneInput(item);
  }
  async add(item) {
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      if (this.items.has(item.id)) {
        throw new Error(`TaskInput already exists: ${item.id}`);
      }
      const stored = cloneInput(item);
      const next = new Map(this.items);
      next.set(stored.id, stored);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(stored);
    });
  }
  /** Single-row rebind; multi-row same-task rebind uses {@link rebindOpenSessions}. */
  async rebindSession(id, workspaceId, taskPath, sessionId) {
    const [row] = await this.rebindOpenSessions(workspaceId, taskPath, sessionId, [id]);
    if (!row) throw new Error(`TaskInput not found: ${id}`);
    return row;
  }
  /**
   * Atomic batch rebind of open TaskInputs (pending/failed/processing): validate all,
   one persist, all-or-none. `processing` included for replaceSession mid-FIFO claims.
   */
  async rebindOpenSessions(workspaceId, taskPath, sessionId, inputIds) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error("TaskInput.rebindOpenSessions requires workspaceId and taskPath");
    }
    const nextSession = sessionId?.trim();
    if (!nextSession) {
      throw new Error("TaskInput.rebindOpenSessions requires non-empty sessionId");
    }
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const isRebindable = (status) => status === "pending" || status === "failed" || status === "processing";
      let targets;
      if (inputIds !== void 0) {
        targets = [];
        for (const rawId of inputIds) {
          const id = rawId?.trim();
          if (!id) throw new Error("TaskInput.rebindOpenSessions requires non-empty input ids");
          const item = this.items.get(id);
          if (!item || item.workspaceId !== workspaceId || item.taskPath !== taskPath) {
            throw new Error(`TaskInput not found: ${id}`);
          }
          if (!isRebindable(item.status)) {
            throw new Error(
              `TaskInput.rebindOpenSessions requires pending, failed, or processing status; got ${item.status}: ${id}`
            );
          }
          targets.push(item);
        }
      } else {
        targets = [...this.items.values()].filter(
          (i) => isRebindable(i.status) && i.workspaceId === workspaceId && i.taskPath === taskPath
        );
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      let anyChange = false;
      const next = new Map(this.items);
      const rebound = [];
      for (const item of targets) {
        if (item.sessionId === nextSession) {
          rebound.push(cloneInput(item));
          continue;
        }
        anyChange = true;
        const updated = { ...item, sessionId: nextSession, updatedAt: now };
        next.set(item.id, updated);
        rebound.push(cloneInput(updated));
      }
      if (anyChange) {
        await this.persistSnapshot(next);
        this.items = next;
      }
      return rebound;
    });
  }
  /** Test helper: fail the next persistSnapshot once (proves atomic rebind). */
  setNextPersistErrorForTests(error) {
    this.nextPersistErrorForTests = error;
  }
  /**
   * Claim a pending/failed row for background managed inject: → processing.
   * Clears prior lastError/failedAt. Fail-loud if missing or not open for claim.
   */
  async markProcessing(id) {
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.status !== "pending" && item.status !== "failed") {
        throw new Error(
          `TaskInput.markProcessing requires pending or failed; got ${item.status}: ${id}`
        );
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const nextRow = {
        ...item,
        status: "processing",
        updatedAt: now
      };
      delete nextRow.lastError;
      delete nextRow.failedAt;
      delete nextRow.uncertainAt;
      const next = new Map(this.items);
      next.set(id, nextRow);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(nextRow);
    });
  }
  /**
   * Mark managed inject success: pending|processing → delivered.
   * Optional sessionId persists the session that actually received the inject
   * after an exact Task Session replacement/rebind.
   */
  async markDelivered(id, resolvedBy = "service", opts) {
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.status !== "pending" && item.status !== "processing") {
        throw new Error(`TaskInput already ${item.status}: ${id}`);
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const injectSession = opts?.sessionId?.trim();
      const resolved = {
        ...item,
        ...injectSession ? { sessionId: injectSession } : {},
        status: "delivered",
        updatedAt: now,
        deliveredAt: now,
        resolvedBy
      };
      delete resolved.lastError;
      delete resolved.failedAt;
      delete resolved.uncertainAt;
      const next = new Map(this.items);
      next.set(id, resolved);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(resolved);
    });
  }
  /**
   * Mark managed inject failure: processing|pending → failed (never drop).
   * Retained for poll visibility, diagnostics, and later retry enqueue.
   * Do not use when the provider already accepted the inject — use markUncertain.
   */
  async markFailed(id, error, resolvedBy = "service") {
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.status !== "pending" && item.status !== "processing" && item.status !== "failed") {
        throw new Error(
          `TaskInput.markFailed requires open status; got ${item.status}: ${id}`
        );
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const message2 = (error || "managed inject failed").trim() || "managed inject failed";
      const resolved = {
        ...item,
        status: "failed",
        updatedAt: now,
        failedAt: now,
        lastError: message2,
        resolvedBy
      };
      delete resolved.uncertainAt;
      const next = new Map(this.items);
      next.set(id, resolved);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(resolved);
    });
  }
  /**
   * At-most-once uncertain result: provider inject already succeeded, but durable
   * markDelivered failed. Terminal for re-inject — not listRetryableForTask,
   * not cancel-eligible,
   * not markPendingForRetry. Survives restart as uncertain (never reloads as pending).
   */
  async markUncertain(id, error, resolvedBy = "service", opts) {
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.status !== "pending" && item.status !== "processing") {
        throw new Error(
          `TaskInput.markUncertain requires pending or processing; got ${item.status}: ${id}`
        );
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const message2 = (error || "managed inject ok but result confirmation failed").trim() || "managed inject ok but result confirmation failed";
      const injectSession = opts?.sessionId?.trim();
      const resolved = {
        ...item,
        ...injectSession ? { sessionId: injectSession } : {},
        status: "uncertain",
        updatedAt: now,
        uncertainAt: now,
        lastError: message2,
        resolvedBy
      };
      delete resolved.failedAt;
      const next = new Map(this.items);
      next.set(id, resolved);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(resolved);
    });
  }
  /**
   * Re-open a failed (or stuck) row as pending for retry. Does not re-inject.
   * Uncertain is refused — already-sent rows must not re-enter the inject path.
   */
  async markPendingForRetry(id, workspaceId, taskPath) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "TaskInput.markPendingForRetry requires workspaceId and taskPath"
      );
    }
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.workspaceId !== workspaceId || item.taskPath !== taskPath) {
        throw new Error(`TaskInput not found: ${id}`);
      }
      if (item.status === "uncertain") {
        throw new Error(
          `TaskInput.markPendingForRetry refuses uncertain (at-most-once; already sent): ${id}`
        );
      }
      if (item.status !== "failed" && item.status !== "pending") {
        throw new Error(
          `TaskInput.markPendingForRetry requires failed or pending; got ${item.status}: ${id}`
        );
      }
      if (item.status === "pending") return cloneInput(item);
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const nextRow = {
        ...item,
        status: "pending",
        updatedAt: now
      };
      const next = new Map(this.items);
      next.set(id, nextRow);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(nextRow);
    });
  }
  /**
   * External agent formal ack: pending|failed|delivered|uncertain → consumed.
   * Scoped by workspaceId+taskPath; fail-loud on unknown id, scope mismatch, or terminal.
   * Mid-inject processing cannot be acked (wait for injection/fail/uncertain).
   * Ack of uncertain is cleanup only — it does not re-inject.
   */
  async ack(id, workspaceId, taskPath, resolvedBy) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "TaskInput.ack requires workspaceId and taskPath (no id-only ack)"
      );
    }
    if (this.closed) throw new Error("TaskInput store is closed");
    await this.requireHealthy();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("TaskInput store is closed");
      const item = this.items.get(id);
      if (!item) throw new Error(`TaskInput not found: ${id}`);
      if (item.workspaceId !== workspaceId || item.taskPath !== taskPath) {
        throw new Error(`TaskInput not found: ${id}`);
      }
      if (item.status !== "pending" && item.status !== "failed" && item.status !== "delivered" && item.status !== "uncertain") {
        throw new Error(`TaskInput already ${item.status}: ${id}`);
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const resolved = {
        ...item,
        status: "consumed",
        updatedAt: now,
        consumedAt: now,
        resolvedBy,
        ...item.deliveredAt ? { deliveredAt: item.deliveredAt } : {}
      };
      const next = new Map(this.items);
      next.set(id, resolved);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneInput(resolved);
    });
  }
  /**
   * Cancel open (pending/failed) inputs for one (workspace, task).
   * Delivered/processing stay: delivered already processed; processing is in-flight.
   */
  async cancelTask(workspaceId, taskPath, resolvedBy = "service") {
    await this.requireHealthy();
    return this.enqueue(async () => {
      const next = new Map(this.items);
      const cancelled = [];
      const now = (/* @__PURE__ */ new Date()).toISOString();
      for (const item of this.items.values()) {
        if (item.workspaceId !== workspaceId || item.taskPath !== taskPath || !isTaskInputCancelEligibleStatus(item.status)) {
          continue;
        }
        const row = {
          ...item,
          status: "cancelled",
          updatedAt: now,
          cancelledAt: now,
          resolvedBy
        };
        next.set(item.id, row);
        cancelled.push(cloneInput(row));
      }
      if (cancelled.length > 0) {
        await this.persistSnapshot(next);
        this.items = next;
      }
      return cancelled;
    });
  }
  /**
   * Cancel open (pending/failed) inputs bound to a session.
   * Never rewrites delivered/consumed/processing rows.
   */
  async cancelSession(sessionId, resolvedBy = "service") {
    await this.requireHealthy();
    return this.enqueue(async () => {
      const next = new Map(this.items);
      const cancelled = [];
      const now = (/* @__PURE__ */ new Date()).toISOString();
      for (const item of this.items.values()) {
        if (item.sessionId !== sessionId || !isTaskInputCancelEligibleStatus(item.status)) {
          continue;
        }
        const row = {
          ...item,
          status: "cancelled",
          updatedAt: now,
          cancelledAt: now,
          resolvedBy
        };
        next.set(item.id, row);
        cancelled.push(cloneInput(row));
      }
      if (cancelled.length > 0) {
        await this.persistSnapshot(next);
        this.items = next;
      }
      return cancelled;
    });
  }
  shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    this.closed = true;
    this.shutdownPromise = this.corruptError ? Promise.resolve() : this.ensureLoaded().then(
      () => void 0,
      (error) => {
        if (error === this.corruptError) return;
        throw error;
      }
    );
    return this.shutdownPromise;
  }
  async persistSnapshot(snapshot) {
    if (this.nextPersistErrorForTests) {
      const err = this.nextPersistErrorForTests;
      this.nextPersistErrorForTests = null;
      throw err;
    }
    const items = [...snapshot.values()];
    const open5 = items.filter(
      (i) => i.status === "pending" || i.status === "processing" || i.status === "failed" || i.status === "uncertain" || i.status === "delivered"
    );
    const terminal = items.filter((i) => i.status === "consumed" || i.status === "cancelled").sort(
      (a, b) => (b.consumedAt || b.cancelledAt || b.updatedAt || "").localeCompare(
        a.consumedAt || a.cancelledAt || a.updatedAt || ""
      )
    ).slice(0, 100);
    await this.writeState(this.file, { items: [...open5, ...terminal] });
  }
  throwIfCorrupt() {
    if (this.corruptError) throw this.corruptError;
  }
  async requireHealthy() {
    await this.ensureLoaded();
    this.throwIfCorrupt();
  }
  async latchCorrupt(reason) {
    this.throwIfCorrupt();
    this.items.clear();
    this.loaded = true;
    this.corruptError = await persistAuthorityStoreCorruption(
      this.file,
      TASK_INPUT_STORE_CORRUPT,
      reason
    );
  }
};
function makeTaskInputId(rand = Math.random) {
  const alphabet = "0123456789abcdefghjkmnpqrstvwxyz";
  let s = "ti-";
  for (let i = 0; i < 10; i++) s += alphabet[Math.floor(rand() * alphabet.length)];
  return s;
}
function taskInputIdForDecisionRequest(requestId) {
  if (!/^dr-[0-9a-z]{10}$/.test(requestId)) {
    throw new Error("DecisionRequest id must be a canonical dr-* id.");
  }
  return `ti-${requestId.slice(3)}`;
}
function decisionResponseTaskInputText(request) {
  const id = taskInputIdForDecisionRequest(request.id);
  void id;
  const lines = [`requestId: ${request.id}`];
  const response = request.response;
  if (response.kind === "option") {
    const option = request.options.find((candidate) => candidate.id === response.optionId);
    if (!option) throw new Error("DecisionResponse option is missing from its request.");
    lines.push(
      "response: option",
      `optionId: ${response.optionId}`,
      `optionLabel: ${JSON.stringify(option.label)}`
    );
  } else if (response.kind === "custom") {
    lines.push("response: custom", `text: ${JSON.stringify(response.text)}`);
  } else {
    lines.push("response: deny");
  }
  return lines.join("\n");
}
function formatTaskInputPrompt(input) {
  const kind = normalizeTaskInputKind(input.kind);
  if (kind === "review-feedback") {
    const lines2 = [
      "## Review Feedback",
      `inputId: ${input.id}`,
      `taskPath: ${input.taskPath}`,
      `kind: review-feedback`
    ];
    lines2.push(
      `text: ${typeof input.text === "string" ? input.text : ""}`
    );
    if (input.createdAt) lines2.push(`createdAt: ${input.createdAt}`);
    lines2.push(
      "",
      "Lifecycle-generated review feedback for the same task after reject-resume. Not chat history. Do not invent prior messages. Final report still goes through TaskResult only."
    );
    return lines2.join("\n");
  }
  if (kind === "decision-response") {
    return [
      "## Decision Response",
      `inputId: ${input.id}`,
      "kind: decision-response",
      input.text?.trim() || ""
    ].join("\n");
  }
  const lines = [
    "## User Input",
    `inputId: ${input.id}`,
    `taskPath: ${input.taskPath}`
  ];
  if (input.text) lines.push(`text: ${input.text}`);
  if (input.contextRefs?.length) {
    lines.push(`contextRefs: ${input.contextRefs.join(", ")}`);
  }
  if (input.createdAt) lines.push(`createdAt: ${input.createdAt}`);
  lines.push(
    "",
    "One-shot user append to the running task. Not chat history. Do not invent prior messages. Final report still goes through TaskResult only."
  );
  return lines.join("\n");
}

// src/service/decision-request-flow.ts
var IMMUTABLE_TASK_INPUT_FIELDS = [
  "id",
  "workspaceId",
  "taskPath",
  "taskId",
  "sessionId",
  "role",
  "kind",
  "text"
];
function assertDecisionResponseTaskInputMatches(existing, expected) {
  for (const field of IMMUTABLE_TASK_INPUT_FIELDS) {
    if (existing[field] !== expected[field]) {
      throw new Error(
        `DecisionRequest TaskInput ${expected.id} conflicts on immutable field ${field}.`
      );
    }
  }
}
function prepareDecisionResponse(input) {
  const request = validateDecisionRequest(input.request);
  const { binding } = input;
  if (request.taskId !== binding.taskId) {
    throw new Error(
      `DecisionRequest Task mismatch: expected ${request.taskId}, got ${binding.taskId}.`
    );
  }
  if (request.requester.id !== binding.sessionId) {
    throw new Error(
      `DecisionRequest Session mismatch: expected ${request.requester.id}, got ${binding.sessionId}.`
    );
  }
  if (!Number.isFinite(Date.parse(input.now))) {
    throw new Error("DecisionRequest response timestamp must be an ISO date.");
  }
  const response = validateDecisionResponse(input.response, request.options);
  const answered = request.status === "pending" ? answerDecisionRequest(request, input.responder, response) : (() => {
    if (request.resolvedBy.kind !== input.responder.kind || request.resolvedBy.id !== input.responder.id || JSON.stringify(request.response) !== JSON.stringify(response)) {
      throw new Error(`Decision request already answered differently: ${request.id}.`);
    }
    return request;
  })();
  const taskInput = {
    id: taskInputIdForDecisionRequest(answered.id),
    workspaceId: binding.workspaceId,
    taskPath: binding.taskPath,
    taskId: binding.taskId,
    sessionId: binding.sessionId,
    ...binding.role ? { role: binding.role } : {},
    kind: "decision-response",
    text: decisionResponseTaskInputText(answered),
    status: "pending",
    createdAt: input.now,
    updatedAt: input.now
  };
  return { answered, taskInput };
}

// src/service/rpc-error.ts
var RpcError = class extends Error {
  constructor(code, message2, data) {
    super(message2);
    this.name = "RpcError";
    this.code = code;
    this.data = data;
  }
};

// src/service/workspace-collaboration.ts
async function buildWorkspaceCollaborationProjection(input) {
  const tasksById = indexCanonicalTasks(input.tasks);
  const { byTaskId: decisionsByTaskId, actionable: actionableDecisions } = await selectActionableUserDecisions(input, tasksById);
  const resultsById = indexTaskResultsById(input.results);
  const readyResultsByTaskId = indexReadyTaskResults(input.results);
  const inboxItems = selectUserInboxResults(input.tasks, resultsById, tasksById);
  for (const { request, task } of actionableDecisions) {
    inboxItems.push({
      kind: "decision",
      requestId: request.id,
      taskId: task.id,
      nodeIds: [...task.nodeIds],
      question: request.question,
      options: request.options.map((option) => ({ ...option })),
      createdAt: request.createdAt
    });
  }
  inboxItems.sort(compareWorkspaceUserInboxItem);
  let selectedNode = null;
  if (input.nodeId) {
    const selectedTasks = listDirectActiveTasksForNode(input.nodeId, input.tasks);
    for (const selectedTask of selectedTasks) {
      if (!selectedTask.id) {
        throw consistencyError("Selected active Task is missing canonical id", {
          nodeId: input.nodeId
        });
      }
      const sameId = tasksById.get(selectedTask.id) ?? [];
      if (sameId.length !== 1) {
        throw consistencyError("Selected active Task identity is ambiguous", {
          nodeId: input.nodeId,
          taskId: selectedTask.id
        });
      }
    }
    const activeTasks = [];
    for (const selectedTask of selectedTasks) {
      activeTasks.push(
        await projectActiveTask({
          ...input,
          task: selectedTask,
          resultsById,
          readyResults: selectedTask.id ? readyResultsByTaskId.get(selectedTask.id) ?? [] : [],
          decisionByTaskId: decisionsByTaskId
        })
      );
    }
    selectedNode = {
      nodeId: input.nodeId,
      activeTasks,
      statusDetail: selectNodeStatusDetail(input.nodeId, input.tasks)
    };
  }
  const counts = { result: 0, decision: 0, total: inboxItems.length };
  for (const item of inboxItems) counts[item.kind] += 1;
  return {
    workspaceId: input.workspaceId,
    selectedNode,
    inbox: { items: inboxItems, counts }
  };
}
function selectNodeStatusDetail(nodeId, tasks) {
  const candidates = tasks.filter((task) => task.nodeIds.includes(nodeId));
  const ids = /* @__PURE__ */ new Set();
  for (const task of candidates) {
    if (!task.id || !isTaskId(task.id)) {
      throw consistencyError("Node return Task is missing canonical identity", { nodeId });
    }
    if (ids.has(task.id)) {
      throw consistencyError("Node return Task identity is ambiguous", {
        nodeId,
        taskId: task.id
      });
    }
    ids.add(task.id);
    if (typeof task.updatedAt !== "string" || !Number.isFinite(Date.parse(task.updatedAt))) {
      throw consistencyError("Node return Task timestamp is invalid", {
        nodeId,
        taskId: task.id
      });
    }
  }
  candidates.sort(
    (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt) || left.id.localeCompare(right.id)
  );
  const selected = candidates[0];
  return selected?.statusDetail ? { taskId: selected.id, ...selected.statusDetail } : null;
}
function indexCanonicalTasks(tasks) {
  const tasksById = /* @__PURE__ */ new Map();
  for (const task of tasks) {
    if (!task.id) continue;
    const sameId = tasksById.get(task.id) ?? [];
    sameId.push(task);
    tasksById.set(task.id, sameId);
  }
  return tasksById;
}
async function selectActionableUserDecisions(input, tasksById) {
  const byTaskId = /* @__PURE__ */ new Map();
  const actionable = [];
  for (const request of input.pendingDecisions) {
    if (request.target.kind !== "user") continue;
    const sameId = tasksById.get(request.taskId) ?? [];
    const exactPath = sameId.filter((candidate) => candidate.path === request.taskPath);
    const candidates = exactPath.filter(
      (candidate) => candidate.state === "waiting" && candidate.wait?.reason === "user-input" && candidate.wait.code === `decision_request:${request.id}` && candidate.executionSessionId === request.requester.id
    );
    if (candidates.length === 0) continue;
    if (sameId.length !== 1 || candidates.length !== 1) {
      throw consistencyError("Actionable user DecisionRequest has ambiguous Task identity", {
        requestId: request.id,
        taskId: request.taskId
      });
    }
    if (byTaskId.has(request.taskId)) {
      throw consistencyError("Task has multiple pending user DecisionRequests", {
        taskId: request.taskId
      });
    }
    const task = candidates[0];
    const session = await input.readSession(request.requester.id);
    if (!session || !session.open || session.workspace !== input.workspaceId || session.currentTaskId !== task.id) {
      continue;
    }
    byTaskId.set(request.taskId, request);
    actionable.push({ request, task });
  }
  return { byTaskId, actionable };
}
function indexReadyTaskResults(results) {
  const index2 = /* @__PURE__ */ new Map();
  for (const result of results) {
    if (result.status !== "ready") continue;
    const sameTask = index2.get(result.taskId) ?? [];
    sameTask.push(result);
    index2.set(result.taskId, sameTask);
  }
  return index2;
}
function indexTaskResultsById(results) {
  const index2 = /* @__PURE__ */ new Map();
  for (const result of results) {
    const sameId = index2.get(result.id) ?? [];
    sameId.push(result);
    index2.set(result.id, sameId);
  }
  return index2;
}
function selectUserInboxResults(tasks, resultsById, tasksById) {
  const items = [];
  for (const task of tasks) {
    if (task.state !== "submitted") continue;
    if (!task.requester) {
      throw consistencyError("Submitted Task lacks responsibility", {
        taskId: task.id ?? null
      });
    }
    if (task.requester.kind !== "user") continue;
    if (task.requester.id !== "user") {
      throw consistencyError("Submitted Task has invalid user responsibility", {
        taskId: task.id ?? null
      });
    }
    if (!task.id) {
      throw consistencyError("User-reviewable Task is missing canonical id", {});
    }
    const sameId = tasksById.get(task.id) ?? [];
    if (sameId.length !== 1) {
      throw consistencyError("User-reviewable Task identity is ambiguous", {
        taskId: task.id
      });
    }
    const sameResultId = task.currentResultId ? resultsById.get(task.currentResultId) ?? [] : [];
    if (sameResultId.length !== 1) {
      throw consistencyError("User-reviewable Task Result identity is not unique", {
        taskId: task.id,
        resultId: task.currentResultId ?? null,
        matches: sameResultId.length
      });
    }
    const result = sameResultId[0];
    if (!result || result.status !== "ready" || result.taskId !== task.id || result.path !== taskResultPathForTask(task.path, result.id)) {
      throw consistencyError("User-reviewable Task Result identity is stale", {
        resultId: task.currentResultId ?? null,
        taskId: task.id,
        taskState: task.state,
        currentResultId: task.currentResultId ?? null
      });
    }
    items.push({
      kind: "result",
      resultId: result.id,
      taskId: result.taskId,
      summary: result.report,
      createdAt: requireTaskResultCreatedAt(result, task.id)
    });
  }
  return items;
}
async function projectActiveTask(input) {
  const taskId = input.task.id;
  if (!taskId) {
    throw consistencyError("Selected Task is missing canonical id", {});
  }
  const responsibility = projectResponsibility(input.task, input.roles);
  const execution = await projectExecution(input, taskId);
  const readyResult = projectSelectedReadyResult(input, taskId);
  const decision = input.decisionByTaskId.get(taskId);
  return {
    taskId,
    state: input.task.state,
    responsibility,
    execution,
    readyResult,
    pendingDecision: decision ? projectDecision(decision) : null
  };
}
function projectResponsibility(task, roles) {
  const taskId = task.id;
  if (!task.requester) {
    throw consistencyError("Selected Task lacks parent responsibility", { taskId });
  }
  if (task.requester.kind === "user") {
    if (task.requester.id !== "user") {
      throw consistencyError("Task has invalid user responsibility", {
        taskId,
        requesterId: task.requester.id
      });
    }
    return { kind: "user" };
  }
  const role = roles.find((candidate) => candidate.id === task.requester.id);
  if (!role) {
    throw consistencyError("Task parent responsibility Role is missing", {
      taskId,
      roleId: task.requester.id
    });
  }
  return {
    kind: "role",
    roleId: task.requester.id,
    displayName: role.displayName?.trim() || role.name
  };
}
async function projectExecution(input, taskId) {
  if (input.task.assigneeRoleId) {
    const role = input.roles.find((candidate) => candidate.id === input.task.assigneeRoleId);
    if (!role) {
      throw consistencyError("Task assignee Role is missing", {
        taskId,
        roleId: input.task.assigneeRoleId
      });
    }
    return {
      kind: "role",
      roleId: input.task.assigneeRoleId,
      displayName: role.displayName?.trim() || role.name
    };
  }
  if (input.task.executionSessionId) {
    const session = await input.readSession(input.task.executionSessionId);
    if (!session || session.workspace !== input.workspaceId || session.currentTaskId !== taskId) {
      throw consistencyError("Selected Task Session binding is stale", { taskId });
    }
    if (!session.connectionId) return null;
    const connection = input.getConnection(session.connectionId);
    if (!connection) {
      throw consistencyError("Selected Task references a missing Agent Connection", {
        taskId,
        connectionId: session.connectionId
      });
    }
    return {
      kind: "connection",
      connectionId: connection.connectionId,
      displayName: connection.displayName?.trim() || connection.connectionId
    };
  }
  if (input.task.state === "queued") return null;
  throw consistencyError("Active Task has no exact assignee", { taskId });
}
function projectSelectedReadyResult(input, taskId) {
  if (input.readyResults.length > 1) {
    throw consistencyError("Selected Task has multiple ready Task Results", {
      taskId,
      resultIds: input.readyResults.map((result) => result.id)
    });
  }
  let readyResult = null;
  if (input.task.currentResultId) {
    const sameResultId = input.resultsById.get(input.task.currentResultId) ?? [];
    if (sameResultId.length !== 1) {
      throw consistencyError("Selected Task Result identity is not unique", {
        taskId,
        resultId: input.task.currentResultId,
        matches: sameResultId.length
      });
    }
    const result = sameResultId[0];
    if (result.taskId !== taskId) {
      throw consistencyError("Selected Task Result binding is stale", {
        taskId,
        resultId: input.task.currentResultId
      });
    }
    if (result.status === "ready") {
      if (input.task.state !== "submitted") {
        throw consistencyError("Selected Task has a ready Result outside submitted state", {
          taskId,
          taskState: input.task.state,
          resultId: result.id
        });
      }
      readyResult = {
        resultId: result.id,
        summary: result.report,
        createdAt: requireTaskResultCreatedAt(result, taskId)
      };
    }
  }
  if (input.task.state === "submitted") {
    if (!readyResult) {
      throw consistencyError("Submitted selected Task lacks its exact ready Result", {
        taskId,
        currentResultId: input.task.currentResultId ?? null
      });
    }
    if (input.readyResults.length !== 1 || input.readyResults[0].id !== readyResult.resultId) {
      throw consistencyError("Submitted selected Task has inconsistent ready Result authority", {
        taskId,
        currentResultId: readyResult.resultId
      });
    }
  } else if (input.readyResults.length > 0) {
    throw consistencyError("Selected Task has an unbound ready Result", {
      taskId,
      taskState: input.task.state,
      resultId: input.readyResults[0].id
    });
  }
  return readyResult;
}
function requireTaskResultCreatedAt(result, taskId) {
  const createdAt = result.createdAt;
  if (typeof createdAt !== "string" || createdAt.trim() !== createdAt || !Number.isFinite(Date.parse(createdAt))) {
    throw consistencyError("Current ready Task Result lacks durable createdAt", {
      taskId,
      resultId: result.id
    });
  }
  return createdAt;
}
function projectDecision(request) {
  return {
    requestId: request.id,
    question: request.question,
    options: request.options.map((option) => ({ ...option }))
  };
}
function compareWorkspaceUserInboxItem(a, b) {
  const byTime = a.createdAt.localeCompare(b.createdAt);
  if (byTime !== 0) return byTime;
  const byKind = a.kind.localeCompare(b.kind);
  if (byKind !== 0) return byKind;
  const aId = a.kind === "result" ? a.resultId : a.requestId;
  const bId = b.kind === "result" ? b.resultId : b.requestId;
  return aId.localeCompare(bId);
}
function consistencyError(message2, data) {
  return new RpcError(-32010, `workspace.collaboration consistency error: ${message2}`, {
    code: "WORKSPACE_COLLABORATION_STALE",
    ...data
  });
}

// src/service/types.ts
var PROVIDER_VERIFICATION_LEVELS = [
  "adapter-implemented",
  "mock-tested",
  "opt-in-live-probe",
  "live-verified"
];
var CLIENT_METHODS = [
  "service.health",
  "service.subscribe",
  "workspace.mount",
  "workspace.unmount",
  "workspace.list",
  "workspace.setForeground",
  /** Selected Node collaboration + user-actionable Inbox; read-only product join. */
  "workspace.collaboration",
  /**
   * Workspace collaboration settings (system-root settings.json).
   * settings is a read projection; settings.update is user-only MutationBus.
   * Successful actual mutation emits exactly one workspace.settings.updated; no-op emits none.
   */
  "workspace.settings",
  "workspace.settings.update",
  /**
   * Canonical workspace-root AGENTS.md (not under .tent).
   * Fixed filename only — no arbitrary path. Missing file projects empty content + exists=false.
   * write is user-only MutationBus with optional baseEtag conflict protection (docs style).
   * Successful actual mutation emits exactly one workspace.agents.updated; no-op emits none.
   */
  "workspace.agents",
  "workspace.agents.write",
  "docs.list",
  "docs.get",
  "docs.readForEdit",
  /** Existing Node body/frontmatter write; requires baseEtag (-32008 missing / -32009 conflict). */
  "docs.write",
  "docs.createNote",
  "docs.fork",
  /**
   * User-only atomic node rename (MutationBus).
   * Keeps cx- immutable; renames folder + identity note; rewrites path links.
   * Success emits exactly one node.changed with oldPath/path.
   */
  "docs.rename",
  /**
   * User-only structural move / reparent (MutationBus).
   * Resolve moved node + destination by cx-; require expectedPath (stale → -32009 path_stale).
   * Reparent rewrites path links; same-parent reorder is order-only.
   * Success emits exactly one node.changed (reason docs.move) with oldPath/path/pathMap.
   * Canonical name is docs.move — no docs.reparent alias.
   */
  "docs.move",
  /**
   * Set Node mode (editable | archived). Sole mode mutation RPC.
   * Ordinary docs.write cannot set mode/id/collaboration reserved fields.
   */
  "docs.setMode",
  "docs.search",
  "docs.backlinks",
  /**
   * Import binary attachment for a node.
   * Wire: base64 string in the canonical `bytesBase64` field.
   * Disk: original bytes under attachments/<cx>/… — never a .b64 text companion.
   */
  "docs.importAttachment",
  /**
   * User-only Node type mutation (MutationBus + baseEtag).
   * Public semantic path for one optional arbitrary Node type marker; not via free-form docs.write.
   * Success emits exactly one node.changed with reason docs.setType.
   */
  "docs.setType",
  /**
   * User-only Node tags replace (MutationBus + baseEtag).
   * Empty array clears Node tags without pruning the global registry.
   * Success emits exactly one node.changed with reason docs.tags.set.
   */
  "docs.tags.set",
  /**
   * User-only attach one tag to a Node (MutationBus + baseEtag; idempotent).
   * Auto-registers new names into tags.json. Success emits node.changed reason docs.tag.add.
   */
  "docs.tag.add",
  /**
   * User-only detach one tag from a Node (MutationBus + baseEtag).
   * Does not prune the global registry. Success emits node.changed reason docs.tag.remove.
   */
  "docs.tag.remove",
  /**
   * First-class semantic Node relations (source frontmatter `relations` array).
   * list is read-only; create/update/delete are user-only MutationBus + source baseEtag.
   * Not Markdown/wiki body links; kind is an open identifier (no registry).
   */
  "relation.list",
  "relation.create",
  "relation.update",
  "relation.delete",
  /** Read-only global tag vocabulary (tags.json). */
  "registry.tags",
  /**
   * User-only ensure a tag exists in the global vocabulary (MutationBus).
   * Success emits registry.tags.updated (even when already present — client may no-op on payload).
   */
  "registry.tag.create",
  /**
   * User-only global tag delete + cascade off all Nodes (MutationBus).
   * Success emits exactly one registry.tags.updated (no per-Node node.changed).
   * Clients must treat that event as invalidating tag candidates and graph/node
   * projections whose tags may have been rewritten by the cascade.
   */
  "registry.tag.delete",
  "registry.roles",
  /**
   * User-only role registry mutations (MutationBus).
   * Persist id/name/displayName/prompt/description/color —
   * never provider secrets. id is server-assigned and immutable; displayName is
   * mutable; operational name is not renamed in identity batch 1.
   * Success emits exactly one registry.roles.updated.
   */
  "registry.role.create",
  "registry.role.update",
  "registry.role.delete",
  "connection.list",
  "connection.get",
  "connection.create",
  "connection.update",
  "connection.delete",
  /**
   * Read-only product provider verification catalog.
   * Params: none (machine-global product facts; not workspace-scoped).
   * Result: { providers: ProviderCatalogEntry[] } — adapterId + verificationLevel
   * (+ optional canResume/notes). Never secrets, env values, or Connection config.
   * Distinct from connection.* (machine-local launch config).
   */
  "provider.catalog",
  /** Machine-local launch secrets (user Settings only; never returns plaintext). */
  "settings.launchSecret.list",
  "settings.launchSecret.set",
  "settings.launchSecret.delete",
  /** Machine-local bundled skill list/install (user surface; no workspaceId). */
  "skill.list",
  "skill.install",
  "task.dispatch",
  "task.claim",
  /**
   * Durable Role self-execution: atomically create + claim from exact nodeIds[].
   * Service derives parent/review authority from persisted Task/Session responsibility;
   * callers cannot provide actor, target, or Task Result authority fields.
   */
  "task.claimDirect",
  "task.wait",
  "task.resume",
  /**
   * Exact executing Session requests one parent/user decision and parks the Task.
   */
  "task.requestDecision",
  /**
   * U2A one-shot append: user-only text and/or contextRefs to a running/waiting task.
   * Not chat; does not answer a pending Decision Request; does not mutate Agent Connections.
   */
  "task.sendInput",
  "task.submit",
  "task.accept",
  "task.reject",
  "task.interrupt",
  "task.cancel",
  "task.startSession",
  /**
   * Explicit fresh managed Session on the same Task (unusable provider context).
   * Never a silent fallback from task.startSession. Uses the same machine Settings
   * Agent Connection availability gate as startSession.
   * Shares the per-Task managed-session execution slot with startSession.
   * Preserves frozen Node context/worktree/branch/lane/pending TaskInputs/acceptMode;
   * stops the old Session first; new ss- has providerContextRestored=false + stable restoreReason.
   * isTurnActive → fail-loud TURN_BUSY (retryable); no force flag in this contract.
   * waiting only when durable waitCode=session_unavailable (not user-input/tool).
   */
  "task.replaceSession",
  "task.list",
  "task.get",
  "task.package",
  /**
   * Explicit post-accept Result provenance binding for existing Output Nodes.
   * Params: workspaceId + exact accepted resultId + outputNodeIds[] + actor.
   * Accepting a Result never calls this automatically.
   */
  "task.bindOutput",
  "taskResult.list",
  "taskResult.get",
  /**
   * V0.2 Output provenance (Output → Task Result → Task).
   * Params: workspaceId + canonical nodeId.
   * Unbound output → bound:false + nulls; corrupt refs → incomplete reasons.
   */
  "output.provenance",
  /**
   * Workspace-level graph projection for Working-set Canvas.
   * Params: workspaceId.
   * Result: { workspaceId, nodes, edges: { parent, markdown, wiki, relation } }.
   * Node summaries only (no body); unresolved markdown/wiki links and relation targets kept explicitly.
   * Semantic relation edges are a separate collection — never merged with parent/markdown/wiki.
   * Placement / view state is never projected or persisted here.
   */
  "graph.projection",
  /** Proposal triage — separate from Task Result review (task-api §3). */
  "proposal.list",
  "proposal.submit",
  "proposal.resolve",
  "session.list",
  "session.get",
  /**
   * External / pull-host session lifecycle (no ACP spawn).
   * enter: register or reuse state=external SessionRegistry row.
   * status: probe + incomplete task bindings for that session.
   * leave: stop/unbind external Session only — never submit/review.
   */
  "session.enter",
  "session.status",
  "session.leave",
  /** ACP tool permission approvals (permissionPolicy=ask). */
  "toolApproval.listPending",
  "toolApproval.get",
  "toolApproval.approveOnce",
  "toolApproval.deny",
  /**
   * Exact-Task Decision Request. Response authority comes from transport context;
   * no caller-provided actor selector is accepted.
   */
  "decisionRequest.listPending",
  "decisionRequest.get",
  "decisionRequest.respond",
  "decisionRequest.escalate",
  /**
   * Unified A2U pending read projection (workspace-scoped).
   * Aggregates user-targeted Decision Requests, ACP tool approval, and
   * status=ready Task Result. No new store / state machine; resolve stays on
   * domain RPCs (decisionRequest.* / toolApproval.* / task.accept|reject).
   * Fail-loud on any source failure — never a partial authoritative inbox.
   */
  "interaction.listPending",
  /**
   * U2A task input (one-shot append) — machine-local; not chat.
   * send is user-only; listPending/get/ack require workspaceId+taskPath (no global inbox).
   * ack actor must match stored task role or service-verified session binding.
   */
  "taskInput.listPending",
  "taskInput.get",
  "taskInput.ack",
  /**
   * Operational retention (task-api §6) — user-only.
   * preview is read-only; purge mutates via MutationBus and emits retention.purged only when files deleted.
   */
  "operationalRetention.preview",
  "operationalRetention.purge",
  /**
   * Read-only diagnostic for one terminal Task's temporary Git worktree reclaim.
   * Params: workspaceId + taskPath. Reclaim is an explicit exact-Task operation;
   * terminal transitions and mount recovery never schedule hidden cleanup.
   */
  "task.worktreeReclaim.preview",
  "task.worktreeReclaim.reconcile",
  /**
   * Node Markdown underline annotations (划线注释) — first-class workspace records.
   * Independent of body markers, Node attributes, and Task. User-only mutations via MutationBus.
   * Events are invalidation only (annotation.changed); never auto-inject Agent / TaskInput.
   * list projects live relocate (anchored|relocated|orphan) without rewriting stored anchors.
   */
  "annotation.list",
  "annotation.create",
  "annotation.resolve",
  "annotation.reopen",
  "annotation.delete"
];
function isClientMethod(method) {
  return CLIENT_METHODS.includes(method);
}
var PROTECTED_COLLAB_FIELDS = ["status", "owner", "assignee"];
var RESERVED_DOCS_WRITE_FIELDS = [
  "id",
  "mode",
  "archived",
  /** Output provenance — only formal task.accept bind path may write. */
  "resultId",
  ...PROTECTED_COLLAB_FIELDS
];
var SEMANTIC_DOCS_WRITE_FIELDS = ["type", "tags", "relations"];
var RPC_UNAUTHORIZED = -32001;
var RPC_LIFECYCLE = -32022;

// src/service/connections.ts
import * as fs14 from "node:fs/promises";
import * as path13 from "node:path";

// src/adapters/types.ts
var ManagedSessionStartupError = class extends Error {
  constructor(cause, managedSession) {
    super(cause instanceof Error ? cause.message : String(cause));
    this.name = "ManagedSessionStartupError";
    this.managedSession = managedSession;
    this.cause = cause;
    if (cause && typeof cause === "object") {
      const source = cause;
      if (typeof source.code === "string") this.code = source.code;
      if (source.terminalAlreadyEmitted === true) {
        this.terminalAlreadyEmitted = true;
      }
    }
  }
};

// src/adapters/fake/index.ts
import * as fs9 from "node:fs";
import * as os2 from "node:os";
import * as path9 from "node:path";
var FAKE_ADAPTER_ID = "fake-cli";
function buildInlineScript(opts) {
  return `
const fs = require('fs');
const sleepMs = ${opts.sleepMs};
const exitCode = ${opts.exitCode};
const waitForSignal = ${opts.waitForSignal ? "true" : "false"};
const emitStdout = ${opts.emitStdout ? "true" : "false"};
const promptFile = process.env.TENT_BOOTSTRAP_FILE || '';
if (emitStdout) {
  let prompt = '';
  try { if (promptFile) prompt = fs.readFileSync(promptFile, 'utf8').slice(0, 200); } catch {}
  process.stdout.write('fake-adapter live' + (prompt ? ' prompt=' + JSON.stringify(prompt) : '') + '\\n');
}
function shutdown(code) {
  try { if (promptFile) fs.unlinkSync(promptFile); } catch {}
  process.exit(code);
}
if (waitForSignal) {
  const onStop = () => shutdown(0);
  process.on('SIGTERM', onStop);
  process.on('SIGINT', onStop);
  // Windows: taskkill / SIGTERM via child.kill maps here when possible.
  setInterval(() => {}, 1 << 30);
} else {
  setTimeout(() => shutdown(exitCode), sleepMs);
}
`.trim();
}
function normalizeFakeOpts(raw) {
  const o = raw && typeof raw === "object" ? raw : {};
  return {
    sleepMs: typeof o.sleepMs === "number" ? o.sleepMs : 3e4,
    exitCode: typeof o.exitCode === "number" ? o.exitCode : 0,
    waitForSignal: o.waitForSignal !== false,
    emitStdout: o.emitStdout !== false,
    failLaunch: o.failLaunch,
    canResume: o.canResume === true
  };
}
var FakeProviderAdapter = class {
  constructor(options = {}) {
    this.id = FAKE_ADAPTER_ID;
    this.displayNameKey = "adapter.fake.displayName";
    this.nodePath = options.nodePath ?? process.execPath;
  }
  capabilities() {
    return {
      canSpawn: true,
      canResume: false,
      // default; a route session may store a resume token separately
      canStopGraceful: true,
      needsTty: false,
      supportsWorktreeCwd: true,
      authModel: "none",
      observeLevel: "process"
    };
  }
  resolveLaunch(plan) {
    const fake = normalizeFakeOpts(plan.extras?.fake ?? plan.extras);
    if (fake.failLaunch) {
      throw new Error(fake.failLaunch);
    }
    let bootstrapFile;
    if (plan.bootstrapPrompt != null && plan.bootstrapPrompt.length > 0) {
      bootstrapFile = path9.join(
        os2.tmpdir(),
        `tent-bootstrap-${plan.sessionId.replace(/[^a-zA-Z0-9_-]/g, "")}.txt`
      );
      fs9.writeFileSync(bootstrapFile, plan.bootstrapPrompt, "utf8");
    }
    const script = buildInlineScript(fake);
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (bootstrapFile) env.TENT_BOOTSTRAP_FILE = bootstrapFile;
    if (plan.command) {
      return {
        command: plan.command,
        args: plan.args ?? [],
        cwd: plan.cwd,
        env,
        bootstrapFile,
        stopSignal: "SIGTERM"
      };
    }
    return {
      command: this.nodePath,
      args: ["-e", script],
      cwd: plan.cwd,
      env,
      bootstrapFile,
      stopSignal: "SIGTERM"
    };
  }
  parseResumeToken(raw) {
    return { raw, providerSessionId: raw };
  }
  mapExit(code, signal) {
    if (signal && signal !== "SIGTERM" && signal !== "SIGINT") {
      return { type: "session.failed", sessionId: "", error: `signal:${signal}` };
    }
    if (code === 0 || code === null && (signal === "SIGTERM" || signal === "SIGINT")) {
      return { type: "session.exited", sessionId: "", exitCode: code };
    }
    if (code !== 0 && code != null) {
      return {
        type: "session.failed",
        sessionId: "",
        error: `exit:${code}`
      };
    }
    return { type: "session.exited", sessionId: "", exitCode: code };
  }
};
function createFakeAdapter(options) {
  return new FakeProviderAdapter(options);
}

// src/adapters/grok-acp/index.ts
import * as os3 from "node:os";
import * as path11 from "node:path";

// src/adapters/acp/client.ts
import { spawn as spawn3 } from "node:child_process";

// src/runtime/child-env.ts
var RESERVED_TENT_CHILD_ENV_KEYS = [
  "TENT_SERVICE_DATA_DIR",
  "TENT_SERVICE_TOKEN",
  "TENT_SERVICE_URL",
  "TENT_SERVICE_HOST",
  "TENT_SERVICE_PORT",
  "TENT_SESSION_ID",
  "TENT_SESSION_TOKEN"
];
var RESERVED_KEY_SET = new Set(
  RESERVED_TENT_CHILD_ENV_KEYS.map((k) => k.toUpperCase())
);
var COMMON_HOST_ENV_KEYS = [
  "PATH",
  "Path",
  // Windows may expose mixed case
  "TMP",
  "TEMP",
  "TMPDIR",
  // Narrow locale only — enough for libc/node message catalogs, not full user identity.
  "LANG",
  "LC_ALL",
  "LC_CTYPE",
  "TZ"
];
var WIN32_HOST_ENV_KEYS = [
  "PATHEXT",
  "SystemRoot",
  "SYSTEMROOT",
  "windir",
  "WINDIR",
  "ComSpec",
  "COMSPEC",
  "USERPROFILE",
  "HOME",
  // HOMEDRIVE+HOMEPATH together resolve ~ equivalent on some Windows toolchains
  "HOMEDRIVE",
  "HOMEPATH",
  // Windows-native launchers such as npx resolve their cache and installed
  // shims from these directories. Omitting them can redirect writes to an
  // invalid profile-relative fallback even when the package is already cached.
  "LOCALAPPDATA",
  "APPDATA"
];
var POSIX_HOST_ENV_KEYS = ["HOME"];
function isReservedTentChildEnvKey(key2) {
  return RESERVED_KEY_SET.has(key2.toUpperCase());
}
function stripReservedTentChildEnv(env) {
  const out = {};
  if (!env) return out;
  for (const [key2, value] of Object.entries(env)) {
    if (value == null) continue;
    if (isReservedTentChildEnvKey(key2)) continue;
    if (typeof value !== "string") continue;
    out[key2] = value;
  }
  return out;
}
function pickMinimalHostEnv(hostEnv = process.env, platform = process.platform) {
  const keys = /* @__PURE__ */ new Set([
    ...COMMON_HOST_ENV_KEYS,
    ...platform === "win32" ? WIN32_HOST_ENV_KEYS : POSIX_HOST_ENV_KEYS
  ]);
  const out = {};
  for (const key2 of keys) {
    const value = hostEnv[key2];
    if (typeof value === "string" && value.length > 0) {
      out[key2] = value;
    }
  }
  if (platform === "win32" && out.PATH == null && typeof hostEnv.Path === "string") {
    out.PATH = hostEnv.Path;
  }
  return out;
}
function buildManagedChildEnv(options = {}) {
  const platform = options.platform ?? process.platform;
  const host = pickMinimalHostEnv(options.hostEnv ?? process.env, platform);
  const launch = stripReservedTentChildEnv(options.launchEnv);
  const out = {
    ...host,
    ...launch
  };
  for (const key2 of Object.keys(out)) {
    if (isReservedTentChildEnvKey(key2)) delete out[key2];
  }
  if (options.reserved) {
    for (const key2 of RESERVED_TENT_CHILD_ENV_KEYS) {
      const value = options.reserved[key2];
      if (typeof value === "string" && value.length > 0) {
        out[key2] = value;
      }
    }
  }
  return out;
}

// src/adapters/acp/assistant-report.ts
function selectFinalAssistantReport(segments) {
  for (let i = segments.length - 1; i >= 0; i -= 1) {
    const text3 = typeof segments[i] === "string" ? segments[i].trim() : "";
    if (text3) return text3;
  }
  return "";
}
function sealAssistantMessageSegment(segments, current) {
  const body = typeof current === "string" ? current : "";
  if (body.trim()) {
    segments.push(body);
  }
  return { segments, current: "" };
}
function isAssistantMessageChunkKind(kind) {
  return kind === "agent_message_chunk";
}

// src/adapters/acp/redact.ts
var SECRET_ENV_KEY_RE = /^(.*_)?(API[_-]?KEY|TOKEN|SECRET|PASSWORD|ACCESS[_-]?KEY|PRIVATE[_-]?KEY|CREDENTIAL|AUTH)$/i;
var DEFAULT_PLACEHOLDER = "[redacted]";
function isSecretEnvKeyName(key2) {
  if (SECRET_ENV_KEY_RE.test(key2)) return true;
  if (/AUTH_REQUEST/i.test(key2)) return true;
  if (/^TENT_.*_(KEY|TOKEN|SECRET)$/i.test(key2)) return true;
  return false;
}
function collectSecretValues(env, extraSecrets) {
  const found = /* @__PURE__ */ new Set();
  if (env) {
    for (const [key2, value] of Object.entries(env)) {
      if (typeof value !== "string" || !value) continue;
      if (isSecretEnvKeyName(key2) || valueLooksLikeHighEntropySecret(value)) {
        if (isSecretEnvKeyName(key2)) found.add(value);
      }
    }
  }
  if (extraSecrets) {
    for (const value of extraSecrets) {
      if (typeof value === "string" && value.length > 0) found.add(value);
    }
  }
  return [...found].sort((a, b) => b.length - a.length);
}
function valueLooksLikeHighEntropySecret(value) {
  void value;
  return false;
}
function redactSecrets(text3, secrets, placeholder = DEFAULT_PLACEHOLDER, minimumSecretLength = 4) {
  if (!text3 || secrets.length === 0) return text3;
  let out = text3;
  for (const secret of secrets) {
    if (!secret || secret.length < minimumSecretLength) continue;
    if (!out.includes(secret)) continue;
    out = out.split(secret).join(placeholder);
  }
  return out;
}
function redactDiagnosticText(text3, options) {
  const secrets = collectSecretValues(options?.env, options?.secrets);
  return redactSecrets(text3, secrets, options?.placeholder ?? DEFAULT_PLACEHOLDER);
}

// src/adapters/acp/client.ts
var LOAD_REPLAY_QUIET_MS = 100;
var LOAD_REPLAY_MAX_WAIT_MS = 2e3;
var RPC_ERROR_DATA_MAX_CHARS = 600;
var ASSISTANT_CHUNK_PAGE_BYTES = 64 * 1024;
var ASSISTANT_CHUNK_PAGE_ITEMS = 1024;
var RPC_ERROR_SAFE_KEYS = /* @__PURE__ */ new Set([
  "code",
  "kind",
  "message",
  "reason",
  "stderr",
  "type",
  // ACP SDK / bridge internals often put the real failure here while message is
  // the opaque "Internal error" (e.g. Claude Agent ACP session resume).
  "details",
  "errorKind"
]);
function formatRpcError(error, secrets = []) {
  const message2 = error.message || "ACP JSON-RPC error";
  const code = Number.isFinite(error.code) ? ` [JSON-RPC ${error.code}]` : "";
  const data = summarizeRpcErrorData(error.data, secrets);
  return `${message2}${code}${data ? ` (${data})` : ""}`;
}
function summarizeRpcErrorData(data, secrets = []) {
  if (data == null) return void 0;
  if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") {
    const raw = typeof data === "string" ? redactBoundedDiagnosticText(data, secrets, RPC_ERROR_DATA_MAX_CHARS) : String(data);
    return `data=${raw.slice(0, RPC_ERROR_DATA_MAX_CHARS)}`;
  }
  if (typeof data !== "object" || Array.isArray(data)) {
    return `dataType=${Array.isArray(data) ? "array" : typeof data}`;
  }
  const safe = {};
  for (const [key2, value] of Object.entries(data)) {
    if (!RPC_ERROR_SAFE_KEYS.has(key2)) continue;
    if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
      safe[key2] = value == null ? null : typeof value === "string" ? redactBoundedDiagnosticText(
        value,
        secrets,
        RPC_ERROR_DATA_MAX_CHARS
      ).slice(0, RPC_ERROR_DATA_MAX_CHARS) : value;
    }
  }
  if (Object.keys(safe).length === 0) return void 0;
  return `data=${JSON.stringify(safe).slice(0, RPC_ERROR_DATA_MAX_CHARS)}`;
}
function parseAcpAuthMethods(value) {
  if (!Array.isArray(value)) return [];
  return value.flatMap((entry) => {
    if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
    const record = entry;
    if (typeof record.id !== "string" || !record.id.trim()) return [];
    if (record.type !== void 0 && typeof record.type !== "string") return [];
    return [{ ...record, id: record.id }];
  });
}
function assertInBandAuthMethod(methods, methodId) {
  if (typeof methodId !== "string" || !methodId) {
    throw new Error("ACP authenticate hook must select a non-empty methodId");
  }
  const matches = methods.filter((method) => method.id === methodId);
  if (matches.length !== 1) {
    throw new Error(`ACP authenticate hook selected unavailable methodId: ${methodId}`);
  }
  const type = matches[0]?.type ?? "agent";
  if (type !== "agent") {
    throw new Error(
      `ACP auth method ${methodId} has out-of-band or unsupported type: ${type}`
    );
  }
}
function isSessionResumeAdvertised(sessionCapabilities) {
  if (!sessionCapabilities || typeof sessionCapabilities !== "object" || Array.isArray(sessionCapabilities)) {
    return false;
  }
  const resume = sessionCapabilities.resume;
  return resume != null && typeof resume === "object" && !Array.isArray(resume);
}
var AcpClient = class {
  constructor(options) {
    this.options = options;
    this.proc = null;
    this.nextId = 1;
    this.pending = /* @__PURE__ */ new Map();
    /**
     * Sealed assistant message segments for the in-flight session/prompt.
     * Contiguous agent_message_chunk text forms one segment; tool/status/thought
     * (and any other non-message update) seals the open segment. TaskResult summary
     * is the last non-empty segment (see selectFinalAssistantReport).
     */
    this.assistantMessageSegments = [];
    /** Fixed-size joined pages keep fragment overhead bounded by report bytes. */
    this.assistantMessageCurrentChunks = [];
    this.assistantMessageCurrentPages = [];
    this.assistantMessageCurrentPageBytes = 0;
    this.assistantReportBytes = 0;
    this.consecutiveNoProgressUpdates = 0;
    this.diagnosticEventsEmitted = 0;
    this.diagnosticEventsSuppressed = 0;
    this.stdoutFrameBytes = 0;
    this.stderrTail = "";
    this.closed = false;
    this.stopRequested = false;
    /** Dedupe spontaneous exit vs prompt-failure / intentional stop terminal events. */
    this.terminalEmitted = false;
    this.exitCode = null;
    this.exitSignal = null;
    this.childExited = false;
    this.exitWaiters = [];
    /**
     * Only chunks received while our own session/prompt request is pending belong
     * to the next result. Load replay (including notifications arriving after
     * the load response) and unsolicited provider updates stay diagnostic-only.
     */
    this.collectingPromptResponse = false;
    /** Defensive quarantine for bridges that resolve load before their final replay notification. */
    this.quarantiningLoadReplay = false;
    /** Buffer config replacements until the current new/load/resume response baseline lands. */
    this.bufferingSessionStartConfig = false;
    this.lastLoadReplayUpdateAt = 0;
    /**
     * Bootstrap image refs are projected on the first managed session/prompt only.
     * Follow-up / resume prompts must not re-send image bytes (one-shot contract).
     */
    this.bootstrapImagesProjected = false;
    /** Cached from initialize agentCapabilities.loadSession (default false). */
    this.loadSessionSupported = false;
    /**
     * Cached from initialize agentCapabilities.sessionCapabilities.resume
     * (object including `{}`; default false until connect).
     */
    this.resumeSessionSupported = false;
    /**
     * Cached from initialize agentCapabilities.promptCapabilities.image.
     * Only explicit true counts; omit/false → unsupported (no guessing).
     */
    this.promptImageSupported = false;
    /** Latest complete bounded Agent-owned Session configuration state. */
    this.sessionConfigSnapshot = createAcpSessionConfigSnapshot({});
    /** Concurrent ask-policy requests keep the session waiting until all resolve. */
    this.permissionAsksInFlight = 0;
    /** Stop/exit cancellation for in-flight onPermissionAsk waiters. */
    this.permissionWaitCancels = /* @__PURE__ */ new Set();
    this.label = typeof options.label === "string" && options.label.trim() ? options.label.trim() : "ACP";
    this.resourceLimits = resolveAcpResourceLimits(options.resourceLimits);
    this.acpObservation = {
      permissionRequestCount: 0,
      permissionPolicy: options.permissionPolicy,
      spontaneousChildExit: false
    };
    const diagnosticSecrets = this.secretValues();
    this.stderrDiagnosticRedactor = new BoundedDiagnosticRedactor(
      diagnosticSecrets,
      ACP_DIAGNOSTIC_EVENT_BYTES
    );
    this.updateDiagnosticRedactor = new BoundedDiagnosticRedactor(
      diagnosticSecrets,
      ACP_DIAGNOSTIC_EVENT_BYTES
    );
  }
  /**
   * Build session/new or session/load params from the start/resume snapshot.
   * mcpServers always present (array; may be empty).
   * Skill name/path refs go under `_meta.tent.skills` as Tent metadata only
   * (not a guarantee that every provider activates skills from this field).
   * Must not log returned params (mcpServers may hold secret values).
   */
  sessionStartParams(base) {
    const mcpServers = Array.isArray(this.options.mcpServers) ? this.options.mcpServers : [];
    const params = {
      ...base,
      mcpServers
    };
    const skills = Array.isArray(this.options.skills) ? this.options.skills : [];
    if (skills.length > 0) {
      params._meta = {
        tent: {
          skills
        }
      };
    }
    return params;
  }
  get pid() {
    return this.proc?.pid ?? void 0;
  }
  get providerSession() {
    return this.providerSessionId;
  }
  get lastAssistantText() {
    return this.finalizeAssistantReport();
  }
  get lastStderrTail() {
    return this.stderrTail;
  }
  get sessionConfig() {
    return cloneAcpSessionConfigSnapshot(this.sessionConfigSnapshot);
  }
  createSessionConfigSnapshot(input) {
    const secrets = this.secretValues();
    const containsSecret = (value) => secrets.some(
      (secret) => secret.length > 0 && value.includes(secret)
    );
    const marker = ["[redacted]", "<hidden>", "***", "_"].find((candidate) => !containsSecret(candidate));
    return createAcpSessionConfigSnapshot({
      ...input,
      ...secrets.length > 0 ? {
        scrubDisplayText: (value) => {
          if (!containsSecret(value)) return value;
          if (!marker) return void 0;
          const scrubbed = redactSecrets(value, secrets, marker, 1);
          return containsSecret(scrubbed) ? marker : scrubbed;
        },
        containsSecret
      } : {}
    });
  }
  replaceSessionConfigOptions(projected) {
    return this.commitSessionConfigOptions(
      this.projectSessionConfigOptions(projected)
    );
  }
  projectSessionConfigOptions(projected) {
    const nextSnapshot = {
      ...this.sessionConfigSnapshot,
      configOptions: projected.configOptions,
      truncated: this.sessionConfigSnapshot.truncated || projected.truncated
    };
    const changed = JSON.stringify(this.sessionConfigSnapshot.configOptions) !== JSON.stringify(nextSnapshot.configOptions) || this.sessionConfigSnapshot.truncated !== nextSnapshot.truncated;
    return { snapshot: nextSnapshot, changed };
  }
  commitSessionConfigOptions(candidate) {
    if (candidate.changed) this.sessionConfigSnapshot = candidate.snapshot;
    return candidate.changed;
  }
  /** Keep the last complete replacement plus structural loss seen before it. */
  accumulateSessionStartConfigOptions(projected) {
    this.pendingSessionStartConfigSnapshot = {
      ...projected,
      truncated: (this.pendingSessionStartConfigSnapshot?.truncated ?? false) || projected.truncated
    };
  }
  /**
   * True only when initialize advertised agentCapabilities.promptCapabilities.image === true.
   * Default false until connect(); custom/unclear transports stay false.
   */
  get supportsPromptImage() {
    return this.promptImageSupported;
  }
  isAlive() {
    const pid = this.proc?.pid;
    if (pid == null || pid <= 0 || this.childExited) return false;
    try {
      process.kill(pid, 0);
      return true;
    } catch {
      return false;
    }
  }
  /**
   * Seal any open segment and return the managed result report for this turn:
   * last non-empty assistant message segment (not intermediate narrations).
   */
  finalizeAssistantReport() {
    this.sealOpenAssistantSegment();
    return selectFinalAssistantReport(this.assistantMessageSegments);
  }
  /** Reset per-prompt accumulation (never mix reconnect/retry chunks). */
  resetAssistantReport() {
    this.assistantMessageSegments = [];
    this.assistantMessageCurrentChunks = [];
    this.assistantMessageCurrentPages = [];
    this.assistantMessageCurrentPageBytes = 0;
    this.assistantReportBytes = 0;
    this.consecutiveNoProgressUpdates = 0;
    this.lastObservableControlFingerprint = void 0;
    this.diagnosticEventsEmitted = 0;
    this.diagnosticEventsSuppressed = 0;
  }
  appendAssistantMessageChunk(text3, bytes) {
    this.assistantMessageCurrentChunks.push(text3);
    this.assistantMessageCurrentPageBytes += bytes;
    if (this.assistantMessageCurrentPageBytes >= ASSISTANT_CHUNK_PAGE_BYTES || this.assistantMessageCurrentChunks.length >= ASSISTANT_CHUNK_PAGE_ITEMS) {
      this.flushAssistantMessagePage();
    }
  }
  flushAssistantMessagePage() {
    if (this.assistantMessageCurrentChunks.length === 0) return;
    this.assistantMessageCurrentPages.push(
      this.assistantMessageCurrentChunks.join("")
    );
    this.assistantMessageCurrentChunks = [];
    this.assistantMessageCurrentPageBytes = 0;
  }
  /**
   * Non-message session/update kinds seal the open assistant segment so later
   * message chunks become a new final-report candidate.
   */
  sealOpenAssistantSegment() {
    if (this.assistantMessageCurrentPages.length === 0 && this.assistantMessageCurrentChunks.length === 0) {
      return;
    }
    this.flushAssistantMessagePage();
    const current = this.assistantMessageCurrentPages.join("");
    this.assistantMessageCurrentPages = [];
    this.assistantMessageCurrentChunks = [];
    this.assistantMessageCurrentPageBytes = 0;
    if (!current.trim()) return;
    if (this.assistantMessageSegments.length >= this.resourceLimits.assistantSegments) {
      this.triggerLimit(
        ACP_OUTPUT_LIMIT_CODE,
        `assistant segment count exceeds ${this.resourceLimits.assistantSegments}`
      );
      return;
    }
    const sealed = sealAssistantMessageSegment(
      this.assistantMessageSegments,
      current
    );
    this.assistantMessageSegments = sealed.segments;
  }
  /**
   * Spawn ACP process + initialize/authenticate, then session/new, session/load,
   * or session/resume. Emits session.live when the ACP session exists. Does not
   * block on prompt.
   *
   * - Load mode requires agentCapabilities.loadSession === true. History
   *   notifications are quarantined and never enter assistantText / result.
   * - Resume mode requires agentCapabilities.sessionCapabilities.resume (object,
   *   including `{}`). Does not replay history (Tent is not a transcript UI).
   * Both load and resume fail loud and never fall back to session/new.
   */
  async connect(options) {
    const mode = options?.mode === "load" || options?.mode === "resume" ? options.mode : "new";
    this.spawnProcess();
    const pid = this.proc.pid;
    this.options.emit({
      type: "session.starting",
      sessionId: this.options.sessionId
    });
    try {
      const init = await this.request("initialize", {
        protocolVersion: 1,
        clientCapabilities: {
          fs: { readTextFile: false, writeTextFile: false },
          terminal: false,
          session: { configOptions: { boolean: {} } }
        }
      });
      this.loadSessionSupported = init.agentCapabilities?.loadSession === true;
      this.resumeSessionSupported = isSessionResumeAdvertised(
        init.agentCapabilities?.sessionCapabilities
      );
      this.promptImageSupported = acpTransportSupportsImage(
        init.agentCapabilities
      );
      if (this.options.authenticate) {
        const authMethods = parseAcpAuthMethods(init.authMethods);
        const authParams = await this.options.authenticate(
          authMethods
        );
        assertInBandAuthMethod(authMethods, authParams.methodId);
        const meta = authParams._meta && typeof authParams._meta === "object" && !Array.isArray(authParams._meta) ? { ...authParams._meta, headless: true } : { headless: true };
        await this.request("authenticate", {
          ...authParams,
          _meta: meta
        });
      }
      this.sessionConfigSnapshot = this.createSessionConfigSnapshot({
        agentCapabilities: init.agentCapabilities,
        authMethods: init.authMethods
      });
      let providerSessionId;
      this.bufferingSessionStartConfig = true;
      this.pendingSessionStartConfigSnapshot = void 0;
      try {
        if (mode === "load" || mode === "resume") {
          const method = mode === "resume" ? "session/resume" : "session/load";
          if (mode === "load" && !this.loadSessionSupported) {
            throw new Error(
              `${this.label} does not advertise agentCapabilities.loadSession; cannot session/load`
            );
          }
          if (mode === "resume" && !this.resumeSessionSupported) {
            throw new Error(
              `${this.label} does not advertise agentCapabilities.sessionCapabilities.resume; cannot session/resume`
            );
          }
          const loadId = typeof options?.providerSessionId === "string" ? options.providerSessionId.trim() : "";
          if (!loadId) {
            throw new Error(
              `${this.label} ${method} requires providerSessionId (resume token)`
            );
          }
          this.resetAssistantReport();
          if (mode === "load") {
            this.quarantiningLoadReplay = true;
            this.lastLoadReplayUpdateAt = Date.now();
          }
          const loaded = await this.request(
            method,
            this.sessionStartParams({
              sessionId: loadId,
              cwd: this.options.cwd
            }),
            6e4
          );
          this.replaceSessionConfigOptions(
            this.createSessionConfigSnapshot({
              configOptions: loaded.configOptions
            })
          );
          if (mode === "load") {
            await this.waitForLoadReplayQuiescence();
            this.quarantiningLoadReplay = false;
          }
          this.providerSessionId = loadId;
          providerSessionId = loadId;
        } else {
          const session = await this.request(
            "session/new",
            this.sessionStartParams({ cwd: this.options.cwd }),
            6e4
          );
          if (!session.sessionId) {
            throw new Error(`${this.label} session/new \u672A\u8FD4\u56DE sessionId`);
          }
          this.providerSessionId = session.sessionId;
          providerSessionId = session.sessionId;
          this.replaceSessionConfigOptions(
            this.createSessionConfigSnapshot({
              configOptions: session.configOptions
            })
          );
        }
        const pending = this.pendingSessionStartConfigSnapshot;
        this.pendingSessionStartConfigSnapshot = void 0;
        this.bufferingSessionStartConfig = false;
        if (pending) this.replaceSessionConfigOptions(pending);
      } finally {
        this.bufferingSessionStartConfig = false;
        this.quarantiningLoadReplay = false;
        this.pendingSessionStartConfigSnapshot = void 0;
        if (mode === "load" || mode === "resume") {
          this.resetAssistantReport();
        }
      }
      this.options.emit({
        type: "session.live",
        sessionId: this.options.sessionId,
        pid
      });
      return {
        pid,
        providerSessionId,
        loadSessionSupported: this.loadSessionSupported,
        resumeSessionSupported: this.resumeSessionSupported,
        sessionConfig: this.sessionConfig
      };
    } catch (err) {
      const failure = isAcpLimitError(err) ? err : this.limitError ?? err;
      try {
        await this.stop("shutdown");
      } catch (stopError) {
        if (this.isAlive()) throw stopError;
      }
      if (isAcpLimitError(failure)) throw failure;
      const message2 = failure instanceof Error ? failure.message : String(failure);
      const detail = this.stderrTail ? `${message2} (stderr: ${this.stderrTail.slice(-500)})` : message2;
      throw new Error(this.boundedRedactedDiagnostic(detail));
    }
  }
  /**
   * Send session/prompt with managed bootstrap (Context Card + user prompt).
   * Optional image refs are projected only when live initialize advertised
   * promptCapabilities.image === true; otherwise Markdown pointers + a short note.
   * Collects agent_message_chunk segments; result report is the last non-empty
   * segment after tool/status/thought separators (shared assistant-report contract).
   * Safe to call after connect(); failures throw (caller emits session.failed).
   */
  async sendPrompt(bootstrapPrompt) {
    if (!this.providerSessionId) {
      throw new Error(`${this.label} session \u5C1A\u672A\u5EFA\u7ACB\uFF0C\u65E0\u6CD5 prompt`);
    }
    const pid = this.proc?.pid;
    if (pid == null) {
      throw new Error(`${this.label} \u8FDB\u7A0B\u4E0D\u53EF\u7528`);
    }
    this.resetAssistantReport();
    this.collectingPromptResponse = true;
    try {
      const bootstrapBytes = utf8Bytes(bootstrapPrompt);
      if (bootstrapBytes > this.resourceLimits.bootstrapTextBytes) {
        throw this.triggerLimit(
          ACP_REQUEST_LIMIT_CODE,
          `bootstrap text ${bootstrapBytes} bytes exceeds ${this.resourceLimits.bootstrapTextBytes}`
        );
      }
      const promptTimeout = this.options.promptTimeoutMs ?? DEFAULT_PROMPT_TIMEOUT_MS;
      const prompt = Array.isArray(this.options.bootstrapImageRefs) && this.options.bootstrapImageRefs.length > 0 && !this.bootstrapImagesProjected ? await this.buildPromptBlocks(bootstrapPrompt) : [{ type: "text", text: bootstrapPrompt }];
      const result = await this.request(
        "session/prompt",
        {
          sessionId: this.providerSessionId,
          prompt
        },
        promptTimeout
      );
      if (typeof result.stopReason === "string") {
        this.emitAcpObservation({
          promptStopReason: this.boundedRedactedDiagnostic(
            result.stopReason,
            ACP_OBSERVATION_TEXT_BYTES
          )
        });
      }
      if (this.limitError) throw this.limitError;
      if (this.stopRequested) {
        throw new Error("session interrupted before prompt completed");
      }
      const assistantText = this.finalizeAssistantReport();
      if (this.limitError) throw this.limitError;
      return {
        pid,
        providerSessionId: this.providerSessionId,
        stopReason: result.stopReason,
        assistantText
      };
    } catch (err) {
      if (isAcpLimitError(err)) throw err;
      if (this.limitError) throw this.limitError;
      if (this.stopRequested) {
        throw new Error("session interrupted before prompt completed");
      }
      const message2 = err instanceof Error ? err.message : String(err);
      const detail = this.stderrTail ? `${message2} (stderr: ${this.stderrTail.slice(-500)})` : message2;
      throw new Error(this.boundedRedactedDiagnostic(detail));
    } finally {
      this.collectingPromptResponse = false;
      this.flushUpdateDiagnostics();
    }
  }
  /**
   * Project bootstrap text (+ optional image refs) to ACP content blocks.
   * Image bytes are process-scoped for this RPC only — never stored on the client.
   * Sole gate: cached live promptCapabilities.image from initialize.
   * Bootstrap images are one-shot: only the first managed prompt may project them.
   */
  async buildPromptBlocks(bootstrapPrompt) {
    const refs = Array.isArray(this.options.bootstrapImageRefs) ? this.options.bootstrapImageRefs : [];
    if (refs.length === 0 || this.bootstrapImagesProjected) {
      return [{ type: "text", text: bootstrapPrompt }];
    }
    this.bootstrapImagesProjected = true;
    const projected = await projectBootstrapImagesToAcpPrompt({
      bootstrapText: bootstrapPrompt,
      imageRefs: refs,
      transportSupportsImage: this.promptImageSupported,
      readBinaryBounded: this.options.readBootstrapImageBinary,
      systemRoot: this.options.bootstrapImageSystemRoot
    });
    return projected.prompt;
  }
  /** Keep process alive after bootstrap for probe/stop (caller owns lifecycle). */
  stop(reason) {
    if (this.stopPromise) return this.stopPromise;
    const operation = this.stopExclusive(reason);
    this.stopPromise = operation;
    void operation.catch(() => {
      if (this.stopPromise === operation && !this.childExited) {
        this.stopPromise = void 0;
      }
    });
    return operation;
  }
  async stopExclusive(reason) {
    void reason;
    if (this.childExited) {
      if (this.limitError) {
        this.reportFailed(this.limitError.message);
      }
      this.cleanupStreams();
      return;
    }
    this.stopRequested = true;
    this.closed = true;
    this.cancelPermissionWaiters();
    this.rejectAllPending(new Error("session stopped"));
    const proc = this.proc;
    if (!proc) {
      if (this.limitError) {
        this.reportFailed(this.limitError.message);
      }
      this.cleanupStreams();
      return;
    }
    try {
      proc.kill("SIGTERM");
    } catch {
    }
    await this.waitForExitOrForceKill(1500);
    if (!this.childExited) {
      throw new Error(
        this.boundedRedactedDiagnostic(
          `${this.label} stop was not confirmed by child exit`
        )
      );
    }
    if (this.limitError) {
      this.reportFailed(this.limitError.message);
    }
    this.cleanupStreams();
  }
  /**
   * Emit session.failed once (prompt failure / logical error). Dedupes against
   * spontaneous child-exit terminal emission.
   */
  reportFailed(error) {
    if (this.terminalEmitted) return;
    this.terminalEmitted = true;
    this.options.emit({
      type: "session.failed",
      sessionId: this.options.sessionId,
      error: this.boundedRedactedDiagnostic(error)
    });
  }
  /**
   * Emit session.exited once (clean managed completion path). Dedupes against
   * spontaneous child-exit and reportFailed.
   */
  reportExited(exitCode = 0) {
    if (this.terminalEmitted) return;
    this.terminalEmitted = true;
    this.options.emit({
      type: "session.exited",
      sessionId: this.options.sessionId,
      exitCode
    });
  }
  /** Secret values from launch/core env + explicit resolver outputs — diagnostics only. */
  secretValues() {
    const coreSecrets = collectSecretValues(this.options.coreEnv);
    return collectSecretValues(this.options.env, [
      ...this.options.diagnosticSecrets ?? [],
      ...coreSecrets
    ]);
  }
  boundedRedactedDiagnostic(text3, maxBytes = ACP_DIAGNOSTIC_EVENT_BYTES) {
    return redactBoundedDiagnosticText(
      text3,
      this.secretValues(),
      maxBytes
    );
  }
  formatDiagnostic(prefix, text3) {
    const safe = this.updateDiagnosticRedactor.pushText(text3);
    if (!safe) return "";
    return truncateUtf8Text(
      prefix + safe,
      ACP_DIAGNOSTIC_EVENT_BYTES
    );
  }
  emitUpdateDiagnostic(prefix, text3) {
    const diagnostic = this.formatDiagnostic(prefix, text3);
    if (!diagnostic) return;
    if (this.diagnosticEventsEmitted < this.resourceLimits.diagnosticEvents) {
      this.diagnosticEventsEmitted += 1;
      this.options.emit({
        type: "session.stdout_tail",
        sessionId: this.options.sessionId,
        text: diagnostic
      });
      return;
    }
    this.diagnosticEventsSuppressed += 1;
  }
  flushUpdateDiagnostics() {
    const tail = this.updateDiagnosticRedactor.flush();
    const suppressed = this.diagnosticEventsSuppressed;
    this.diagnosticEventsSuppressed = 0;
    if (!tail && suppressed === 0) return;
    const summary = suppressed > 0 ? `[session/update] ${suppressed} diagnostic fragments suppressed by bounded fan-out
` : "";
    this.options.emit({
      type: "session.stdout_tail",
      sessionId: this.options.sessionId,
      text: truncateUtf8Text(summary + tail, ACP_DIAGNOSTIC_EVENT_BYTES)
    });
  }
  recordNoProgressUpdate() {
    this.consecutiveNoProgressUpdates += 1;
    if (this.consecutiveNoProgressUpdates <= this.resourceLimits.noProgressUpdates) {
      return true;
    }
    this.triggerLimit(
      ACP_OUTPUT_LIMIT_CODE,
      `session/update made no observable progress for more than ${this.resourceLimits.noProgressUpdates} consecutive events`
    );
    return false;
  }
  recordUpdateProgress() {
    this.consecutiveNoProgressUpdates = 0;
  }
  recordContentProgress() {
    this.recordUpdateProgress();
    this.lastObservableControlFingerprint = void 0;
  }
  recordObservableControlProgress(fingerprint) {
    if (fingerprint === this.lastObservableControlFingerprint) {
      return this.recordNoProgressUpdate();
    }
    this.lastObservableControlFingerprint = fingerprint;
    this.recordUpdateProgress();
    return true;
  }
  spawnProcess() {
    const env = buildManagedChildEnv({
      launchEnv: this.options.env,
      reserved: this.options.coreEnv
    });
    const child = spawn3(this.options.command, this.options.args, {
      cwd: this.options.cwd,
      env,
      stdio: ["pipe", "pipe", "pipe"],
      windowsHide: true,
      detached: false
    });
    if (child.pid == null) {
      throw new Error(
        `\u65E0\u6CD5\u542F\u52A8 ${this.label} \u8FDB\u7A0B: ${this.options.command} ${this.options.args.join(" ")}`
      );
    }
    this.proc = child;
    child.stdin?.on("error", (err) => {
      this.rejectAllPending(
        new Error(`${this.label} stdin \u5199\u5165\u5931\u8D25: ${err.message}`)
      );
    });
    child.stderr?.on("data", (chunk) => {
      const text3 = this.stderrDiagnosticRedactor.pushBuffer(chunk);
      if (!text3) return;
      this.stderrTail = appendUtf8Tail(this.stderrTail, text3, 4e3);
      this.options.emit({
        type: "session.stdout_tail",
        sessionId: this.options.sessionId,
        text: text3
      });
    });
    child.stderr?.on("end", () => {
      const text3 = this.stderrDiagnosticRedactor.flush();
      if (!text3) return;
      this.stderrTail = appendUtf8Tail(this.stderrTail, text3, 4e3);
      this.options.emit({
        type: "session.stdout_tail",
        sessionId: this.options.sessionId,
        text: text3
      });
    });
    child.stdout?.on("data", (chunk) => this.onStdoutData(chunk));
    child.stdout?.on("end", () => this.resetStdoutFrame());
    child.on("exit", (code, signal) => {
      this.childExited = true;
      this.exitCode = code;
      this.exitSignal = signal;
      this.closed = true;
      this.cancelPermissionWaiters();
      this.rejectAllPending(
        new Error(
          signal ? `${this.label} \u8FDB\u7A0B\u4FE1\u53F7\u9000\u51FA: ${signal}` : `${this.label} \u8FDB\u7A0B\u9000\u51FA code=${code}`
        )
      );
      if (!this.stopRequested && !this.terminalEmitted) {
        this.emitAcpObservation({
          spontaneousChildExit: true,
          exitCode: boundedAcpExitCode(code),
          ...typeof signal === "string" && signal ? {
            signal: truncateUtf8Text(
              signal,
              ACP_OBSERVATION_SIGNAL_BYTES
            )
          } : {}
        });
        this.terminalEmitted = true;
        if (signal && signal !== "SIGTERM" && signal !== "SIGINT" || code !== 0 && code != null) {
          this.options.emit({
            type: "session.failed",
            sessionId: this.options.sessionId,
            error: signal ? `${this.label} spontaneous exit signal:${signal}` : `${this.label} spontaneous exit code=${code}`
          });
        } else {
          this.options.emit({
            type: "session.exited",
            sessionId: this.options.sessionId,
            exitCode: code
          });
        }
      }
      for (const w of this.exitWaiters) w();
      this.exitWaiters = [];
    });
    child.on("error", (err) => {
      this.closed = true;
      this.cancelPermissionWaiters();
      this.rejectAllPending(
        new Error(`${this.label} \u8FDB\u7A0B\u9519\u8BEF: ${err.message}`)
      );
      if (!this.stopRequested && !this.terminalEmitted) {
        this.terminalEmitted = true;
        this.options.emit({
          type: "session.failed",
          sessionId: this.options.sessionId,
          error: `${this.label} \u8FDB\u7A0B\u9519\u8BEF: ${err.message}`
        });
      }
    });
  }
  onLine(line) {
    let message2;
    try {
      message2 = JSON.parse(line);
    } catch {
      return;
    }
    if ("method" in message2 && message2.method === "session/update") {
      this.handleSessionUpdate(
        message2.params?.update
      );
      return;
    }
    if ("method" in message2 && message2.method === "session/request_permission" && message2.id !== void 0) {
      void this.handlePermissionRequest(
        message2.id,
        message2.params
      );
      return;
    }
    if ("method" in message2 && message2.id !== void 0 && message2.method) {
      this.write({
        jsonrpc: "2.0",
        id: message2.id,
        error: {
          code: -32601,
          message: `Client-side requests are disabled for Tent ${this.label} adapter.`
        }
      });
      return;
    }
    if (!("id" in message2) || message2.id === void 0) return;
    const id = Number(message2.id);
    const pending = this.pending.get(id);
    if (!pending) return;
    this.pending.delete(id);
    clearTimeout(pending.timer);
    if ("error" in message2 && message2.error) {
      pending.reject(new Error(formatRpcError(message2.error, this.secretValues())));
    } else {
      pending.resolve(("result" in message2 ? message2.result : void 0) ?? {});
    }
  }
  handleSessionUpdate(update) {
    if (!update || this.limitError) return;
    const kind = update.sessionUpdate ?? "";
    if (this.quarantiningLoadReplay) this.lastLoadReplayUpdateAt = Date.now();
    if (this.bufferingSessionStartConfig && kind === "config_option_update") {
      if (Array.isArray(update.configOptions)) {
        const projected = this.createSessionConfigSnapshot({
          configOptions: update.configOptions
        });
        const raw = update.configOptions;
        if (raw.length === 0 || projected.configOptions.length > 0 || projected.truncated) {
          this.accumulateSessionStartConfigOptions(projected);
        }
      }
      return;
    }
    if (this.quarantiningLoadReplay) return;
    if (kind === "config_option_update") {
      if (!Array.isArray(update.configOptions)) {
        if (this.collectingPromptResponse) this.recordNoProgressUpdate();
        return;
      }
      const projected = this.createSessionConfigSnapshot({
        configOptions: update.configOptions
      });
      const raw = update.configOptions;
      const droppedOnly = raw.length > 0 && projected.configOptions.length === 0;
      if (droppedOnly && !projected.truncated) {
        if (this.collectingPromptResponse) this.recordNoProgressUpdate();
        return;
      }
      const candidate = this.projectSessionConfigOptions(projected);
      const changed = candidate.changed;
      let observableProgress = false;
      if (this.collectingPromptResponse) {
        let acceptedProgressEvent;
        if (droppedOnly || !changed) {
          acceptedProgressEvent = this.recordNoProgressUpdate();
        } else {
          acceptedProgressEvent = this.recordObservableControlProgress(
            `config:${JSON.stringify({
              configOptions: candidate.snapshot.configOptions,
              truncated: candidate.snapshot.truncated
            })}`
          );
          observableProgress = acceptedProgressEvent;
        }
        if (!acceptedProgressEvent) return;
      }
      if (!changed) return;
      this.commitSessionConfigOptions(candidate);
      if (observableProgress) this.sealOpenAssistantSegment();
      this.options.emit({
        type: "session.config_options",
        sessionId: this.options.sessionId,
        sessionConfig: this.sessionConfig
      });
      return;
    }
    if (!this.collectingPromptResponse) return;
    if (isAssistantMessageChunkKind(kind) && update.content?.text) {
      const chunkBytes = utf8Bytes(update.content.text);
      if (chunkBytes > this.resourceLimits.assistantReportBytes - this.assistantReportBytes) {
        this.triggerLimit(
          ACP_OUTPUT_LIMIT_CODE,
          `assistant report exceeds ${this.resourceLimits.assistantReportBytes} UTF-8 bytes`
        );
        return;
      }
      this.assistantReportBytes += chunkBytes;
      this.appendAssistantMessageChunk(update.content.text, chunkBytes);
      this.recordContentProgress();
      this.emitUpdateDiagnostic(`[${kind}] `, update.content.text);
      return;
    }
    if (kind) {
      this.sealOpenAssistantSegment();
    }
    if (kind === "agent_thought_chunk" && update.content?.text) {
      this.recordContentProgress();
      this.emitUpdateDiagnostic(`[${kind}] `, update.content.text);
      return;
    }
    if (kind === "tool_call" || kind === "tool_call_update") {
      const actualTitle = typeof update.title === "string" && update.title || update.toolCallId || "";
      const status = typeof update.status === "string" ? update.status : "";
      if (actualTitle || status ? !this.recordObservableControlProgress(
        `tool:${update.toolCallId ?? ""}:${actualTitle}:${status}`
      ) : !this.recordNoProgressUpdate()) {
        return;
      }
      const title = actualTitle || "tool";
      const safeTitle = this.boundedRedactedDiagnostic(title, 8192);
      const safeStatus = status ? this.boundedRedactedDiagnostic(status, 4096) : "";
      this.emitUpdateDiagnostic(
        `[${kind}] `,
        `${safeTitle}${safeStatus ? ` (${safeStatus})` : ""}
`
      );
      return;
    }
    if (kind === "status") {
      const status = typeof update.status === "string" ? update.status : "";
      if (status ? !this.recordObservableControlProgress(`status:${status}`) : !this.recordNoProgressUpdate()) {
        return;
      }
      this.emitUpdateDiagnostic("[status] ", status || "status");
      return;
    }
    if (!this.recordNoProgressUpdate()) return;
    if (kind) {
      this.emitUpdateDiagnostic("[session/update] ", kind);
    }
  }
  async waitForLoadReplayQuiescence() {
    const deadline = Date.now() + LOAD_REPLAY_MAX_WAIT_MS;
    while (Date.now() < deadline) {
      const observed = this.lastLoadReplayUpdateAt;
      await sleep(LOAD_REPLAY_QUIET_MS);
      if (this.lastLoadReplayUpdateAt === observed && Date.now() - observed >= LOAD_REPLAY_QUIET_MS) {
        return;
      }
    }
  }
  async handlePermissionRequest(id, params) {
    this.emitAcpObservation({
      permissionRequestCount: Math.min(
        ACP_PERMISSION_REQUEST_COUNT_MAX,
        this.acpObservation.permissionRequestCount + 1
      )
    }, true);
    const options = params.options ?? [];
    const toolTitle = truncateUtf8Text(
      params.toolCall?.title || params.toolCall?.toolCallId || "tool",
      4096
    );
    const toolCallId = typeof params.toolCall?.toolCallId === "string" ? truncateUtf8Text(params.toolCall.toolCallId, 4096) : void 0;
    const policy = this.options.permissionPolicy;
    const tracksAsk = policy === "ask";
    if (tracksAsk) this.permissionAsksInFlight += 1;
    try {
      let decision = "deny";
      if (policy === "allow") {
        decision = "allow";
      } else if (policy === "deny") {
        decision = "deny";
      } else {
        this.options.emit({
          type: "session.waiting_user",
          sessionId: this.options.sessionId,
          summary: `${this.label} \u8BF7\u6C42\u5DE5\u5177\u6743\u9650: ${toolTitle}\uFF08policy=ask\uFF09`
        });
        try {
          if (this.options.onPermissionAsk) {
            const askInfo = { toolTitle, toolCallId, options };
            decision = await this.waitForPermissionDecision(askInfo);
          } else {
            decision = "deny";
          }
        } catch {
          decision = "deny";
        }
      }
      const outcome = decision === "allow" ? selectAllowOnce(options) : { outcome: "cancelled" };
      this.emitAcpObservation({
        permissionDecision: decision,
        permissionOutcome: outcome.outcome === "selected" ? "allow_once" : "cancelled"
      });
      this.write({
        jsonrpc: "2.0",
        id,
        result: { outcome }
      });
      this.options.emit({
        type: "session.stdout_tail",
        sessionId: this.options.sessionId,
        text: this.formatDiagnostic(
          "[permission] ",
          `${toolTitle} \u2192 ${decision === "allow" ? "allow_once" : "deny/cancelled"}
`
        )
      });
    } finally {
      if (tracksAsk) {
        this.permissionAsksInFlight = Math.max(0, this.permissionAsksInFlight - 1);
        if (this.permissionAsksInFlight === 0 && !this.stopRequested && !this.closed) {
          this.options.emit({
            type: "session.live",
            sessionId: this.options.sessionId,
            pid: this.proc?.pid
          });
        }
      }
    }
  }
  waitForPermissionDecision(askInfo) {
    return new Promise((resolve15) => {
      let settled = false;
      const finish = (decision) => {
        if (settled) return;
        settled = true;
        this.permissionWaitCancels.delete(cancel);
        resolve15(decision);
      };
      const cancel = () => finish("deny");
      this.permissionWaitCancels.add(cancel);
      void Promise.resolve().then(() => this.options.onPermissionAsk(askInfo)).then(
        (decision) => finish(decision),
        () => finish("deny")
      );
    });
  }
  cancelPermissionWaiters() {
    for (const cancel of [...this.permissionWaitCancels]) cancel();
  }
  request(method, params, timeoutMs = 3e4) {
    if (this.closed || !this.proc?.stdin) {
      return Promise.reject(
        new Error(`${this.label} \u5DF2\u5173\u95ED\uFF0C\u65E0\u6CD5\u8C03\u7528 ${method}`)
      );
    }
    const id = this.nextId++;
    return new Promise((resolve15, reject) => {
      const timer = setTimeout(() => {
        this.pending.delete(id);
        reject(new Error(`${this.label} ${method} \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`));
      }, timeoutMs);
      this.pending.set(id, { resolve: resolve15, reject, timer });
      this.write({ jsonrpc: "2.0", id, method, params }, (err) => {
        this.failPendingWrite(id, method, err);
      });
    });
  }
  /**
   * Drop a still-pending request after stdin write cannot deliver it.
   * Safe if the id was already settled (response / timeout / rejectAllPending).
   */
  failPendingWrite(id, method, err) {
    const pending = this.pending.get(id);
    if (!pending) return;
    this.pending.delete(id);
    clearTimeout(pending.timer);
    pending.reject(
      new Error(`${this.label} ${method} \u53D1\u9001\u5931\u8D25: ${err.message}`)
    );
  }
  write(payload, onError) {
    const stdin = this.proc?.stdin;
    if (!stdin || stdin.destroyed || !stdin.writable || stdin.writableEnded) {
      onError?.(new Error(`${this.label} stdin \u4E0D\u53EF\u5199`));
      return;
    }
    try {
      const serialized = JSON.stringify(payload);
      const frameBytes = utf8Bytes(serialized);
      if (frameBytes > this.resourceLimits.requestFrameBytes) {
        const error = this.triggerLimit(
          ACP_REQUEST_LIMIT_CODE,
          `outbound JSON-RPC frame ${frameBytes} bytes exceeds ${this.resourceLimits.requestFrameBytes}`
        );
        onError?.(error);
        return;
      }
      stdin.write(serialized + "\n", (err) => {
        if (!err) return;
        onError?.(err instanceof Error ? err : new Error(String(err)));
      });
    } catch (err) {
      onError?.(err instanceof Error ? err : new Error(String(err)));
    }
  }
  rejectAllPending(err) {
    for (const [id, p] of this.pending) {
      clearTimeout(p.timer);
      p.reject(err);
      this.pending.delete(id);
    }
  }
  waitExit() {
    const proc = this.proc;
    if (!proc || this.childExited) {
      return Promise.resolve();
    }
    return new Promise((resolve15) => {
      this.exitWaiters.push(resolve15);
    });
  }
  async waitForExitOrForceKill(timeoutMs) {
    let timer;
    const outcome = await Promise.race([
      this.waitExit().then(() => "exit"),
      new Promise((resolve15) => {
        timer = setTimeout(() => resolve15("timeout"), timeoutMs);
      })
    ]);
    if (timer) clearTimeout(timer);
    if (outcome !== "timeout") return;
    await this.forceKill();
    let confirmTimer;
    const confirmed = await Promise.race([
      this.waitExit().then(() => true),
      new Promise((resolve15) => {
        confirmTimer = setTimeout(() => resolve15(false), timeoutMs);
      })
    ]);
    if (confirmTimer) clearTimeout(confirmTimer);
    if (!confirmed || !this.childExited) {
      throw new Error(
        this.boundedRedactedDiagnostic(
          `${this.label} stop failed: child exit was not confirmed`
        )
      );
    }
  }
  async forceKill() {
    const proc = this.proc;
    const pid = proc?.pid;
    if (!proc || pid == null) return;
    if (process.platform === "win32") {
      await new Promise((resolve15) => {
        let settled = false;
        let timer;
        const finish = () => {
          if (settled) return;
          settled = true;
          if (timer) clearTimeout(timer);
          resolve15();
        };
        const killer = spawn3("taskkill", ["/pid", String(pid), "/T", "/F"], {
          windowsHide: true,
          stdio: "ignore"
        });
        killer.on("exit", finish);
        killer.on("error", finish);
        timer = setTimeout(finish, 1500);
      });
    } else {
      try {
        proc.kill("SIGKILL");
      } catch {
      }
    }
  }
  cleanupStreams() {
    this.proc?.stdout?.removeAllListeners("data");
    this.proc?.stdout?.removeAllListeners("end");
    this.proc?.stderr?.removeAllListeners("data");
    this.proc?.stderr?.removeAllListeners("end");
    this.resetStdoutFrame();
  }
  onStdoutData(chunk) {
    if (this.limitError) return;
    let offset = 0;
    while (offset < chunk.byteLength) {
      const newline = chunk.indexOf(10, offset);
      const end = newline === -1 ? chunk.byteLength : newline;
      const partBytes = end - offset;
      if (partBytes > this.resourceLimits.stdoutFrameBytes - this.stdoutFrameBytes) {
        this.triggerLimit(
          ACP_OUTPUT_LIMIT_CODE,
          `stdout JSON-RPC frame exceeds ${this.resourceLimits.stdoutFrameBytes} bytes before parse`
        );
        return;
      }
      if (partBytes > 0) {
        this.ensureStdoutFrameCapacity(this.stdoutFrameBytes + partBytes);
        chunk.copy(
          this.stdoutFrameBuffer,
          this.stdoutFrameBytes,
          offset,
          end
        );
        this.stdoutFrameBytes += partBytes;
      }
      if (newline === -1) return;
      const frame = this.stdoutFrameBuffer ? this.stdoutFrameBuffer.subarray(0, this.stdoutFrameBytes) : Buffer.alloc(0);
      const withoutCr = frame.byteLength > 0 && frame[frame.byteLength - 1] === 13 ? frame.subarray(0, frame.byteLength - 1) : frame;
      if (withoutCr.byteLength > 0) {
        this.onLine(withoutCr.toString("utf8"));
      }
      this.resetStdoutFrame();
      if (this.limitError) return;
      offset = end + 1;
    }
  }
  ensureStdoutFrameCapacity(requiredBytes) {
    const current = this.stdoutFrameBuffer;
    if (current && current.byteLength >= requiredBytes) return;
    const nextCapacity = Math.min(
      this.resourceLimits.stdoutFrameBytes,
      Math.max(4096, requiredBytes, (current?.byteLength ?? 0) * 2)
    );
    const next = Buffer.allocUnsafe(nextCapacity);
    if (current && this.stdoutFrameBytes > 0) {
      current.copy(next, 0, 0, this.stdoutFrameBytes);
    }
    this.stdoutFrameBuffer = next;
  }
  resetStdoutFrame() {
    this.stdoutFrameBuffer = void 0;
    this.stdoutFrameBytes = 0;
  }
  emitAcpObservation(patch, clearPermissionResult = false) {
    const prior = { ...this.acpObservation };
    if (clearPermissionResult) {
      delete prior.permissionDecision;
      delete prior.permissionOutcome;
    }
    this.acpObservation = { ...prior, ...patch };
    this.options.emit({
      type: "session.acp_observation",
      sessionId: this.options.sessionId,
      observation: { ...this.acpObservation }
    });
  }
  triggerLimit(code, detail) {
    if (this.limitError) return this.limitError;
    const error = new AcpLimitError(code, detail);
    this.limitError = error;
    this.rejectAllPending(error);
    error.terminalAlreadyEmitted = true;
    void this.stop("interrupt").catch(() => void 0);
    return error;
  }
};
function boundedAcpExitCode(value) {
  if (value === null || !Number.isInteger(value)) return null;
  return Math.max(-2147483648, Math.min(2147483647, value));
}
function selectAllowOnce(options) {
  const once = options.find((o) => o.kind === "allow_once") || options.find((o) => o.optionId === "allow_once");
  if (once?.optionId) {
    return { outcome: "selected", optionId: once.optionId };
  }
  return { outcome: "cancelled" };
}
function sleep(ms) {
  return new Promise((resolve15) => setTimeout(resolve15, ms));
}

// src/adapters/acp/managed-session.ts
var DEFAULT_BOOTSTRAP = "Tent session started. Read the task envelope via Tent Task API; do not invent missing content.";
var AcpManagedSession = class {
  constructor(sessionId, client, emit2, stopRequested = false) {
    this.sessionId = sessionId;
    this.client = client;
    this.emit = emit2;
    this.stopRequested = stopRequested;
    /** Depth of in-flight managed session/prompt turns (bootstrap + U2A). */
    this.turnActiveDepth = 0;
    this.bootstrapDone = Promise.resolve();
  }
  get pid() {
    return this.client.pid;
  }
  get providerSessionId() {
    return this.client.providerSession;
  }
  get acpSession() {
    return this.client.sessionConfig;
  }
  isAlive() {
    return this.client.isAlive();
  }
  /**
   * Turn busy ≠ session live. Session may remain alive between turns;
   * busy means a managed prompt is still settling (tools/response not done).
   */
  isTurnActive() {
    return this.turnActiveDepth > 0;
  }
  /** Tests / callers may await bootstrap completion (prompt path finished). */
  async waitBootstrap() {
    await this.bootstrapDone;
  }
  /**
   * U2A follow-up: send a fixed-format user answer as the next session/prompt.
   * Same result rules as bootstrap — end_turn + non-empty text → prompt_complete.
   */
  async sendFollowUpPrompt(prompt) {
    if (this.stopRequested || !this.client.isAlive()) {
      throw new Error(`Managed session not alive for follow-up: ${this.sessionId}`);
    }
    const text3 = prompt.trim();
    if (!text3) throw new Error("sendFollowUpPrompt requires non-empty prompt");
    await this.bootstrapDone;
    const plan = {
      sessionId: this.sessionId,
      connectionId: "",
      cwd: "",
      env: {}
    };
    await this.runTurn(
      () => runManagedBootstrapPrompt(plan, this.emit, this.client, text3)
    );
  }
  async stop(reason) {
    this.stopRequested = true;
    await this.client.stop(
      reason === "user" || reason === "interrupt" || reason === "shutdown" ? reason : "interrupt"
    );
  }
  /**
   * Start a background managed prompt and keep turn-busy until it settles.
   * Used for bootstrap / post-load first prompt (fire-and-forget from startSession).
   */
  beginBackgroundTurn(work) {
    const turn = this.runTurn(work);
    this.bootstrapDone = turn;
    void turn.catch(() => void 0);
    return turn;
  }
  /** Track turn busy/idle around one managed session/prompt. */
  async runTurn(work) {
    this.turnActiveDepth += 1;
    try {
      await work();
    } finally {
      this.turnActiveDepth = Math.max(0, this.turnActiveDepth - 1);
    }
  }
};
function bindAcpPermissionHooks(sessionId, permissionPolicy, hooks) {
  const mapInfo = (info) => ({
    sessionId,
    toolTitle: info.toolTitle,
    toolCallId: info.toolCallId,
    options: (info.options ?? []).map((o) => ({
      optionId: o.optionId,
      kind: o.kind,
      name: o.name
    }))
  });
  return {
    onPermissionAsk: permissionPolicy === "ask" ? async (info) => {
      if (!hooks.onPermissionAsk) return "deny";
      return hooks.onPermissionAsk(mapInfo(info));
    } : void 0
  };
}
function runManagedBootstrapPrompt(plan, emit2, client, bootstrap) {
  return run();
  async function run() {
    let result;
    try {
      result = await client.sendPrompt(bootstrap);
    } catch (err) {
      const message2 = err instanceof Error ? err.message : String(err);
      await failAfterConfirmedStop(
        /interrupted|session stopped/i.test(message2) ? `session interrupted: ${message2}` : message2
      );
      return;
    }
    const stopReason = (result.stopReason || "end_turn").toLowerCase();
    const assistantText = (result.assistantText || "").trim();
    if (stopReason !== "end_turn") {
      await failAfterConfirmedStop(
        `ACP session/prompt stopReason=${result.stopReason || "unknown"} (no auto-result)`
      );
      return;
    }
    if (!assistantText) {
      await failAfterConfirmedStop("ACP assistant response empty (no auto-result)");
      return;
    }
    emit2({
      type: "session.prompt_complete",
      sessionId: plan.sessionId,
      assistantText,
      stopReason: result.stopReason || "end_turn"
    });
  }
  async function failAfterConfirmedStop(message2) {
    await client.stop("interrupt");
    client.reportFailed(message2);
  }
}
async function startManagedAcpSession(input) {
  const { plan, emit: emit2, client } = input;
  const session = new AcpManagedSession(plan.sessionId, client, emit2);
  const bootstrap = plan.bootstrapPrompt !== void 0 ? plan.bootstrapPrompt.trim() : input.defaultBootstrapPrompt?.trim() || DEFAULT_BOOTSTRAP;
  try {
    await client.connect({ mode: "new" });
  } catch (err) {
    if (client.isAlive()) throw new ManagedSessionStartupError(err, session);
    throw err;
  }
  if (bootstrap) {
    session.beginBackgroundTurn(
      () => runManagedBootstrapPrompt(plan, emit2, client, bootstrap)
    );
  }
  return session;
}
async function resumeManagedAcpSession(input) {
  const { plan, emit: emit2, client, providerSessionId } = input;
  const session = new AcpManagedSession(plan.sessionId, client, emit2);
  const loadId = providerSessionId.trim();
  if (!loadId) {
    throw new Error("resumeManagedAcpSession requires non-empty providerSessionId");
  }
  const transport = input.resumeTransport === "resume" ? "resume" : "load";
  const connectMode = transport === "resume" ? "resume" : "load";
  try {
    await client.connect({ mode: connectMode, providerSessionId: loadId });
  } catch (err) {
    if (client.isAlive()) throw new ManagedSessionStartupError(err, session);
    throw err;
  }
  const bootstrap = input.bootstrapPrompt?.trim() || plan.bootstrapPrompt?.trim() || "";
  if (bootstrap) {
    session.beginBackgroundTurn(
      () => runManagedBootstrapPrompt(plan, emit2, client, bootstrap)
    );
  }
  return session;
}
function parseAcpResumeToken(raw) {
  return { raw, providerSessionId: raw };
}
function loadSessionAcpCapabilities(authModel = "external-app") {
  return {
    canSpawn: true,
    canResume: true,
    canStopGraceful: true,
    needsTty: false,
    supportsWorktreeCwd: true,
    authModel,
    observeLevel: "structured"
  };
}
function mapAcpProcessExit(code, signal) {
  if (signal && signal !== "SIGTERM" && signal !== "SIGINT") {
    return { type: "session.failed", sessionId: "", error: `signal:${signal}` };
  }
  if (code === 0 || code === null && (signal === "SIGTERM" || signal === "SIGINT")) {
    return { type: "session.exited", sessionId: "", exitCode: code };
  }
  if (code !== 0 && code != null) {
    return { type: "session.failed", sessionId: "", error: `exit:${code}` };
  }
  return { type: "session.exited", sessionId: "", exitCode: code };
}

// src/fs/node-fs.ts
import * as fs11 from "node:fs/promises";
import * as nodePath5 from "node:path";

// src/fs/mutation-lock.ts
import { randomUUID } from "node:crypto";
import * as fs10 from "node:fs/promises";
var MUTATION_LOCK_STALE_MS = 12e4;
async function withFileMutationLock(lockPath, action, options) {
  const now = options.now ?? Date.now;
  const makeOwnerToken = options.makeOwnerToken ?? randomUUID;
  const staleMs = options.staleMs ?? MUTATION_LOCK_STALE_MS;
  const isProcessAlive = options.isProcessAlive ?? processIsAlive;
  const ownerToken = makeOwnerToken();
  const record = {
    ownerToken,
    pid: process.pid,
    createdAt: new Date(now()).toISOString()
  };
  await fs10.mkdir(dirnameOf(lockPath), { recursive: true });
  let handle;
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      handle = await fs10.open(lockPath, "wx");
      break;
    } catch (error) {
      if (!isAlreadyExists(error)) throw error;
      const reclaimable = await mayReclaimLock(lockPath, now, staleMs, isProcessAlive);
      if (!reclaimable || attempt >= 2) {
        throw new Error(options.busyMessage);
      }
      const quarantine = `${lockPath}.stale-${randomUUID()}`;
      try {
        await fs10.rename(lockPath, quarantine);
        await fs10.rm(quarantine, { force: true }).catch(() => void 0);
      } catch (renameError) {
        if (isNotFound2(renameError)) continue;
        throw renameError;
      }
    }
  }
  if (!handle) throw new Error(options.acquireFailedMessage);
  try {
    await handle.writeFile(JSON.stringify(record), "utf8");
    return await action();
  } finally {
    await handle.close().catch(() => void 0);
    await releaseMutationLockIfOwned(lockPath, ownerToken);
  }
}
async function releaseMutationLockIfOwned(lockPath, ownerToken) {
  const quarantine = `${lockPath}.releasing-${ownerToken}`;
  try {
    await fs10.rename(lockPath, quarantine);
  } catch (error) {
    if (isNotFound2(error)) return false;
    return false;
  }
  const current = await readMutationLockRecord(quarantine);
  if (current?.ownerToken === ownerToken) {
    await fs10.rm(quarantine, { force: true }).catch(() => void 0);
    return true;
  }
  try {
    await fs10.rename(quarantine, lockPath);
  } catch {
    await fs10.rm(quarantine, { force: true }).catch(() => void 0);
  }
  return false;
}
async function readMutationLockRecord(lockPath) {
  try {
    const raw = await fs10.readFile(lockPath, "utf8");
    const value = JSON.parse(raw);
    if (typeof value.ownerToken !== "string" || !value.ownerToken || typeof value.pid !== "number" || !Number.isInteger(value.pid) || typeof value.createdAt !== "string") {
      return null;
    }
    return value;
  } catch (error) {
    if (isNotFound2(error) || error instanceof SyntaxError) return null;
    return null;
  }
}
async function mayReclaimLock(lockPath, now = Date.now, staleMs = MUTATION_LOCK_STALE_MS, isProcessAliveFn = processIsAlive) {
  let mtimeMs;
  try {
    const stat2 = await fs10.stat(lockPath);
    mtimeMs = stat2.mtimeMs;
  } catch (error) {
    if (isNotFound2(error)) return false;
    return true;
  }
  if (now() - mtimeMs <= staleMs) {
    return false;
  }
  const pid = await readRecordedPid(lockPath);
  if (pid !== null && isProcessAliveFn(pid)) {
    return false;
  }
  return true;
}
async function readRecordedPid(lockPath) {
  try {
    const raw = await fs10.readFile(lockPath, "utf8");
    const value = JSON.parse(raw);
    if (typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0) {
      return value.pid;
    }
    return null;
  } catch {
    return null;
  }
}
function processIsAlive(pid) {
  if (!Number.isInteger(pid) || pid <= 0) return false;
  try {
    process.kill(pid, 0);
    return true;
  } catch (error) {
    return !hasCode(error, "ESRCH");
  }
}
function dirnameOf(p) {
  const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
  return i === -1 ? "." : p.slice(0, i);
}
function isAlreadyExists(error) {
  return hasCode(error, "EEXIST");
}
function isNotFound2(error) {
  return hasCode(error, "ENOENT");
}
function hasCode(error, code) {
  return !!error && typeof error === "object" && "code" in error && error.code === code;
}

// src/fs/node-fs.ts
var NodeFs = class {
  constructor(root) {
    this.root = nodePath5.resolve(root);
  }
  abs(p) {
    const resolved = nodePath5.resolve(this.root, p);
    const root = process.platform === "win32" ? this.root.toLowerCase() : this.root;
    const candidate = process.platform === "win32" ? resolved.toLowerCase() : resolved;
    if (candidate !== root && !candidate.startsWith(root + nodePath5.sep)) {
      throw new Error(`Path escapes Tent root: ${p}`);
    }
    return resolved;
  }
  async listDir(dir) {
    const entries = await fs11.readdir(this.abs(dir), { withFileTypes: true });
    return entries.filter((e) => !e.name.startsWith(".git")).map((e) => ({ name: e.name, isDir: e.isDirectory() }));
  }
  async readFile(path23) {
    return fs11.readFile(this.abs(path23), "utf8");
  }
  async writeFile(path23, content3) {
    const abs = this.abs(path23);
    await fs11.mkdir(nodePath5.dirname(abs), { recursive: true });
    await this.atomicReplace(abs, content3, "utf8");
  }
  async readBinary(path23) {
    const buf = await fs11.readFile(this.abs(path23));
    return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
  }
  async readBinaryBounded(path23, maxBytes) {
    if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
      throw new Error(`Invalid binary read limit: ${maxBytes}`);
    }
    const handle = await fs11.open(this.abs(path23), "r");
    try {
      const bytes = new Uint8Array(maxBytes);
      let offset = 0;
      while (offset < maxBytes) {
        const read = await handle.read(bytes, offset, maxBytes - offset, offset);
        if (read.bytesRead === 0) break;
        offset += read.bytesRead;
      }
      const stat2 = await handle.stat();
      return {
        bytes: bytes.subarray(0, offset),
        truncated: stat2.size > offset
      };
    } finally {
      await handle.close();
    }
  }
  async writeBinary(path23, data) {
    const abs = this.abs(path23);
    await fs11.mkdir(nodePath5.dirname(abs), { recursive: true });
    const payload = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
    await this.atomicReplace(abs, payload);
  }
  async atomicReplace(abs, data, encoding) {
    const tmp = `${abs}.tmp-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
    try {
      await fs11.writeFile(tmp, data, encoding);
      await this.renameReplacingWithRetry(tmp, abs);
    } catch (err) {
      await fs11.rm(tmp, { force: true }).catch(() => void 0);
      throw err;
    }
  }
  async renameReplacingWithRetry(from, to) {
    const attempts = process.platform === "win32" ? 10 : 1;
    for (let attempt = 0; attempt < attempts; attempt += 1) {
      try {
        await fs11.rename(from, to);
        return;
      } catch (err) {
        const code = err.code;
        const transient = code === "EPERM" || code === "EACCES" || code === "EBUSY";
        if (!transient || attempt === attempts - 1) throw err;
        const delayMs = Math.min(10 * 2 ** attempt, 100);
        await new Promise((resolve15) => setTimeout(resolve15, delayMs));
      }
    }
  }
  async exists(path23) {
    try {
      await fs11.access(this.abs(path23));
      return true;
    } catch {
      return false;
    }
  }
  async mkdir(path23) {
    await fs11.mkdir(this.abs(path23), { recursive: true });
  }
  async move(from, to) {
    await fs11.mkdir(nodePath5.dirname(this.abs(to)), { recursive: true });
    await fs11.rename(this.abs(from), this.abs(to));
  }
  async remove(path23) {
    await fs11.rm(this.abs(path23), { recursive: true, force: true });
  }
  async withLock(path23, action) {
    return withFileMutationLock(this.abs(path23), action, {
      busyMessage: "Tent is already running another write operation; try again later.",
      acquireFailedMessage: "Cannot acquire the Tent mutation lock."
    });
  }
};

// src/adapters/acp/connection.ts
import * as fs12 from "node:fs";
import * as path10 from "node:path";
function readAcpExtras(extras) {
  if (!extras || typeof extras !== "object") return {};
  if (extras.acp !== void 0) return extras.acp;
  return {};
}
function readAcpSessionProjection(extras) {
  if (!extras || typeof extras !== "object") {
    return { mcpServers: [], skills: [] };
  }
  const mcpRaw = extras.acpMcpServers;
  const skillRaw = extras.acpSkills;
  const mcpServers = Array.isArray(mcpRaw) ? mcpRaw : [];
  const skills = Array.isArray(skillRaw) ? skillRaw : [];
  return { mcpServers, skills };
}
function readCoreChildEnvClientOptions(plan) {
  const out = {};
  if (plan.coreEnv && Object.keys(plan.coreEnv).length > 0) {
    out.coreEnv = plan.coreEnv;
  }
  if (Array.isArray(plan.diagnosticSecrets) && plan.diagnosticSecrets.length > 0) {
    out.diagnosticSecrets = plan.diagnosticSecrets.filter(
      (v) => typeof v === "string" && v.length > 0
    );
  }
  return out;
}
function readBootstrapImageClientOptions(plan) {
  const refs = Array.isArray(plan.bootstrapImageRefs) ? plan.bootstrapImageRefs : [];
  const systemRoot = typeof plan.extras?.bootstrapImageSystemRoot === "string" ? plan.extras.bootstrapImageSystemRoot.trim() : "";
  if (refs.length === 0) {
    return {};
  }
  const out = {
    bootstrapImageRefs: refs
  };
  if (systemRoot) {
    out.bootstrapImageSystemRoot = systemRoot;
    const nodeFs3 = new NodeFs(systemRoot);
    out.readBootstrapImageBinary = (relativePath4, maxBytes) => nodeFs3.readBinaryBounded(relativePath4, maxBytes);
  }
  return out;
}
function normalizeAcpPermissionPolicy(raw) {
  return raw === "allow" || raw === "ask" || raw === "deny" ? raw : "deny";
}
function normalizeSharedAcpOpts(raw) {
  const o = raw && typeof raw === "object" ? raw : {};
  return {
    model: typeof o.model === "string" && o.model.trim() ? o.model.trim() : void 0,
    envKey: typeof o.envKey === "string" && o.envKey.trim() ? o.envKey.trim() : void 0,
    launchSecretRef: typeof o.launchSecretRef === "string" && o.launchSecretRef.trim() ? o.launchSecretRef.trim() : void 0,
    endpoint: typeof o.endpoint === "string" && o.endpoint.trim() ? o.endpoint.trim() : void 0,
    promptTimeoutMs: typeof o.promptTimeoutMs === "number" && o.promptTimeoutMs > 0 ? o.promptTimeoutMs : DEFAULT_PROMPT_TIMEOUT_MS,
    permissionPolicy: normalizeAcpPermissionPolicy(o.permissionPolicy),
    permissionTimeoutMs: typeof o.permissionTimeoutMs === "number" && o.permissionTimeoutMs > 0 ? o.permissionTimeoutMs : DEFAULT_PERMISSION_TIMEOUT_MS
  };
}
function resolvePlanOrProcessEnv(envKey, planEnv, resolve15) {
  if (resolve15) return resolve15(envKey, planEnv);
  const fromPlan = planEnv[envKey];
  if (typeof fromPlan === "string" && fromPlan.trim()) return fromPlan;
  const fromProc = process.env[envKey];
  if (typeof fromProc === "string" && fromProc.trim()) return fromProc;
  return void 0;
}
function defaultNpxLaunch() {
  if (process.platform !== "win32") return { command: "npx", argsPrefix: [] };
  const candidates = [];
  const npmExecPath = process.env.npm_execpath?.trim();
  if (npmExecPath) candidates.push(path10.join(path10.dirname(npmExecPath), "npx-cli.js"));
  for (const dir of (process.env.PATH || "").split(path10.delimiter)) {
    const root = dir.trim().replace(/^"|"$/g, "");
    if (root) candidates.push(path10.join(root, "node_modules", "npm", "bin", "npx-cli.js"));
  }
  const npxCli = candidates.find((candidate) => fs12.existsSync(candidate));
  if (!npxCli) {
    throw new Error(
      "Unable to locate npm/bin/npx-cli.js on Windows; install Node.js/npm or configure an exact Connection command"
    );
  }
  const adjacentNode = path10.resolve(npxCli, "..", "..", "..", "..", "node.exe");
  return {
    command: fs12.existsSync(adjacentNode) ? adjacentNode : "node.exe",
    argsPrefix: [npxCli]
  };
}
function resolveNpxAcpLaunch(input) {
  const command = input.planCommand?.trim();
  if (!command) throw new Error("Agent Connection is missing canonical command");
  if (!Array.isArray(input.planArgs)) {
    throw new Error("Agent Connection is missing canonical args");
  }
  return { command, args: [...input.planArgs] };
}

// src/adapters/grok-acp/client.ts
var GrokAcpClient = class {
  constructor(options) {
    void options.model;
    const acpOptions = {
      command: options.command,
      args: options.args,
      cwd: options.cwd,
      env: options.env,
      coreEnv: options.coreEnv,
      diagnosticSecrets: options.diagnosticSecrets,
      sessionId: options.sessionId,
      promptTimeoutMs: options.promptTimeoutMs,
      permissionPolicy: options.permissionPolicy,
      mcpServers: options.mcpServers,
      skills: options.skills,
      bootstrapImageRefs: options.bootstrapImageRefs,
      bootstrapImageSystemRoot: options.bootstrapImageSystemRoot,
      readBootstrapImageBinary: options.readBootstrapImageBinary,
      label: "Grok ACP",
      emit: options.emit,
      onPermissionAsk: options.onPermissionAsk,
      authenticate: async (authMethods) => {
        const ids = new Set(authMethods.map((m) => m.id));
        const methodId = ids.has("xai.api_key") ? "xai.api_key" : ids.has("cached_token") ? "cached_token" : null;
        if (!methodId) {
          throw new Error(
            "Grok ACP \u672A\u63D0\u4F9B\u53EF\u7528\u7684\u8BA4\u8BC1\u65B9\u5F0F\uFF08\u9700\u8981 xai.api_key \u6216 cached_token\uFF09\u3002\u8BF7\u786E\u8BA4 grok CLI \u4E0E CPA \u914D\u7F6E\u3002"
          );
        }
        return { methodId };
      }
    };
    this.inner = new AcpClient(acpOptions);
  }
  get pid() {
    return this.inner.pid;
  }
  get providerSession() {
    return this.inner.providerSession;
  }
  get sessionConfig() {
    return this.inner.sessionConfig;
  }
  get lastAssistantText() {
    return this.inner.lastAssistantText;
  }
  get lastStderrTail() {
    return this.inner.lastStderrTail;
  }
  isAlive() {
    return this.inner.isAlive();
  }
  connect(options) {
    return this.inner.connect(options);
  }
  sendPrompt(bootstrapPrompt) {
    return this.inner.sendPrompt(bootstrapPrompt);
  }
  stop(reason) {
    return this.inner.stop(reason);
  }
  reportFailed(error) {
    this.inner.reportFailed(error);
  }
  reportExited(exitCode) {
    this.inner.reportExited(exitCode);
  }
};

// src/adapters/grok-acp/types.ts
var GROK_ACP_ADAPTER_ID = "grok-acp";
var DEFAULT_GROK_MODEL = "grok-4.5";
var DEFAULT_GROK_ENV_KEY = "CPA_GROK_API_KEY";
var DEFAULT_GROK_BASE_URL_ENV_KEY = "CPA_GROK_BASE_URL";

// src/adapters/grok-acp/index.ts
function defaultGrokExecutable() {
  if (process.platform === "win32") {
    const home2 = process.env.USERPROFILE || os3.homedir();
    return path11.join(home2, ".grok", "bin", "grok.exe");
  }
  const home = process.env.HOME || os3.homedir();
  return path11.join(home, ".grok", "bin", "grok");
}
function defaultGrokIsolatedHome() {
  const home = process.env.USERPROFILE || process.env.HOME || os3.homedir();
  return path11.join(home, ".grok-acp", "home");
}
var DISABLED_COMPATIBILITY_ENV = [
  "GROK_CLAUDE_SKILLS_ENABLED",
  "GROK_CLAUDE_RULES_ENABLED",
  "GROK_CLAUDE_AGENTS_ENABLED",
  "GROK_CLAUDE_MCPS_ENABLED",
  "GROK_CLAUDE_HOOKS_ENABLED",
  "GROK_CLAUDE_SESSIONS_ENABLED",
  "GROK_CURSOR_SKILLS_ENABLED",
  "GROK_CURSOR_RULES_ENABLED",
  "GROK_CURSOR_AGENTS_ENABLED",
  "GROK_CURSOR_MCPS_ENABLED",
  "GROK_CURSOR_HOOKS_ENABLED",
  "GROK_CURSOR_SESSIONS_ENABLED"
];
function normalizeGrokOpts(raw) {
  const o = raw && typeof raw === "object" ? raw : {};
  const policy = o.permissionPolicy;
  const permissionPolicy = policy === "allow" || policy === "ask" || policy === "deny" ? policy : "deny";
  return {
    model: typeof o.model === "string" && o.model.trim() ? o.model.trim() : DEFAULT_GROK_MODEL,
    envKey: typeof o.envKey === "string" && o.envKey.trim() ? o.envKey.trim() : DEFAULT_GROK_ENV_KEY,
    endpoint: typeof o.endpoint === "string" && o.endpoint.trim() ? o.endpoint.trim() : void 0,
    promptTimeoutMs: typeof o.promptTimeoutMs === "number" && o.promptTimeoutMs > 0 ? o.promptTimeoutMs : DEFAULT_PROMPT_TIMEOUT_MS,
    permissionPolicy,
    permissionTimeoutMs: typeof o.permissionTimeoutMs === "number" && o.permissionTimeoutMs > 0 ? o.permissionTimeoutMs : DEFAULT_PERMISSION_TIMEOUT_MS
  };
}
function normalizeCpaBaseUrl(raw) {
  if (!raw || typeof raw !== "string") return void 0;
  const t = raw.trim().replace(/\/+$/, "");
  return t || void 0;
}
var GrokAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = GROK_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.grokAcp.displayName";
    this.resolveApiKey = options.resolveApiKey ?? ((envKey, planEnv) => planEnv[envKey] ?? process.env[envKey]);
    this.resolveEndpoint = options.resolveEndpoint ?? normalizeCpaBaseUrl;
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("env");
  }
  /**
   * Launch plan validation only. Real ACP needs bidirectional stdio —
   * AgentRuntime uses startManagedSession instead of ProcessSupervisor.
   */
  resolveLaunch(plan) {
    const opts = normalizeGrokOpts(readAcpExtras(plan.extras));
    const command = plan.command?.trim();
    if (!command) throw new Error("Agent Connection is missing canonical command");
    if (!Array.isArray(plan.args)) throw new Error("Agent Connection is missing canonical args");
    const model = opts.model;
    const envKey = opts.envKey;
    const apiKey = this.resolveApiKey(envKey, plan.env);
    const endpoint = this.resolveEndpoint(opts.endpoint);
    if (!apiKey || !apiKey.trim()) {
      throw new Error(
        `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${envKey}\uFF1Agrok-acp \u9700\u8981\u672C\u673A CPA Grok API key\uFF08\u4EC5 service \u8FDB\u7A0B\u73AF\u5883\uFF09\u3002\u4E0D\u4F1A\u56DE\u9000\u5B98\u65B9 xAI\uFF08api.x.ai\uFF09\uFF0C\u4E5F\u4E0D\u4F1A\u56DE\u9000 fake provider\u3002\u8BF7\u5728\u542F\u52A8 Local Service \u524D\u8BBE\u7F6E ${envKey}\uFF1B\u5207\u52FF\u628A key/URL \u5199\u5165 workspace/Node/Task\u3002`
      );
    }
    const args = [...plan.args];
    const env = {
      ...plan.env,
      [envKey]: apiKey,
      // Grok CLI auth method may read XAI_API_KEY; value is the CPA key, not a second secret store.
      XAI_API_KEY: apiKey,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId,
      TENT_GROK_MODEL: model
    };
    if (endpoint) {
      env.XAI_API_BASE_URL = endpoint;
      env.OPENAI_BASE_URL = endpoint;
      env.OPENAI_API_BASE = endpoint;
      env.TENT_GROK_BASE_URL = endpoint;
      env.GROK_MODELS_BASE_URL = endpoint;
      env.GROK_MODELS_LIST_URL = `${endpoint}/models`;
    }
    const isolatedHome = defaultGrokIsolatedHome();
    env.USERPROFILE = isolatedHome;
    env.HOME = isolatedHome;
    env.GROK_HOME = path11.join(isolatedHome, ".grok");
    for (const key2 of DISABLED_COMPATIBILITY_ENV) {
      env[key2] = "false";
    }
    return {
      command,
      args,
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/load (never session/new).
   * Requires agentCapabilities.loadSession on the live initialize handshake.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error("grok-acp resume requires non-empty provider session id");
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeGrokOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const permHooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new GrokAcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      model: opts.model,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      emit: emit2,
      onPermissionAsk: permHooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createGrokAcpAdapter(options) {
  return new GrokAcpProviderAdapter(options);
}

// src/adapters/codex-acp/types.ts
var CODEX_ACP_ADAPTER_ID = "codex-acp";
var CODEX_ACP_NPX_PACKAGE = "@agentclientprotocol/codex-acp";
var CODEX_DEFAULT_AUTH_REQUEST_ENV = "DEFAULT_AUTH_REQUEST";

// src/adapters/codex-acp/index.ts
function buildCodexDefaultAuthRequest(apiKey) {
  return JSON.stringify({
    methodId: "api-key",
    _meta: {
      "api-key": {
        apiKey
      }
    }
  });
}
var CodexAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = CODEX_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.codexAcp.displayName";
    this.resolveEnvValue = options.resolveEnvValue ?? ((envKey, planEnv) => resolvePlanOrProcessEnv(envKey, planEnv));
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("external-app");
  }
  /**
   * Launch plan validation / env injection only.
   * Real ACP needs bidirectional stdio — AgentRuntime uses startManagedSession.
   * Does not call ACP authenticate; injects DEFAULT_AUTH_REQUEST when envKey is set.
   */
  resolveLaunch(plan) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const { command, args } = resolveNpxAcpLaunch({
      planCommand: plan.command,
      planArgs: plan.args
    });
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (opts.envKey) {
      const secret = this.resolveEnvValue(opts.envKey, plan.env);
      if (!secret || !secret.trim()) {
        throw new Error(
          `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${opts.envKey}\uFF1Acodex-acp Agent Connection \u660E\u786E\u8981\u6C42\u8BE5\u5BC6\u94A5\uFF08\u4EC5 service \u8FDB\u7A0B / ConnectionLaunchPlan.env\uFF09\u3002\u8BF7\u8BBE\u7F6E ${opts.envKey} \u540E\u91CD\u8BD5\uFF1B\u5207\u52FF\u628A secret \u5199\u5165 workspace\u3001Node \u6216 Task\u3002`
        );
      }
      env[opts.envKey] = secret;
      env[CODEX_DEFAULT_AUTH_REQUEST_ENV] = buildCodexDefaultAuthRequest(secret);
    }
    return {
      command,
      args,
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/load (never session/new).
   * Requires agentCapabilities.loadSession on the live initialize handshake.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error(
        "codex-acp resume requires non-empty provider session id"
      );
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const permHooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new AcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      label: "Codex ACP",
      emit: emit2,
      onPermissionAsk: permHooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createCodexAcpAdapter(options) {
  return new CodexAcpProviderAdapter(options);
}

// src/adapters/claude-acp/types.ts
var CLAUDE_ACP_ADAPTER_ID = "claude-acp";
var CLAUDE_ACP_NPX_VERSION = "0.62.0";
var CLAUDE_ACP_NPX_PACKAGE = `@agentclientprotocol/claude-agent-acp@${CLAUDE_ACP_NPX_VERSION}`;

// src/adapters/claude-acp/index.ts
var ClaudeAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = CLAUDE_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.claudeAcp.displayName";
    this.resolveEnvValue = options.resolveEnvValue ?? ((envKey, planEnv) => resolvePlanOrProcessEnv(envKey, planEnv));
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("external-app");
  }
  /**
   * Launch plan validation / optional env injection only.
   * Real ACP needs bidirectional stdio — AgentRuntime uses startManagedSession.
   * Does not call ACP authenticate; depends on local Claude login or injected env.
   */
  resolveLaunch(plan) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const { command, args } = resolveNpxAcpLaunch({
      planCommand: plan.command,
      planArgs: plan.args
    });
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (opts.envKey) {
      const secret = this.resolveEnvValue(opts.envKey, plan.env);
      if (!secret || !secret.trim()) {
        throw new Error(
          `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${opts.envKey}\uFF1Aclaude-acp Agent Connection \u660E\u786E\u8981\u6C42\u8BE5\u5BC6\u94A5\uFF08\u4EC5 service \u8FDB\u7A0B / ConnectionLaunchPlan.env\uFF09\u3002\u8BF7\u8BBE\u7F6E ${opts.envKey} \u540E\u91CD\u8BD5\uFF1B\u5207\u52FF\u628A secret \u5199\u5165 workspace/Node/Task\u3002`
        );
      }
      env[opts.envKey] = secret;
    }
    return {
      command,
      args,
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/resume (never session/new,
   * never session/load). Claude Agent ACP 0.62+ advertises sessionCapabilities.resume
   * and implements resume without replaying the full transcript; session/load
   * additionally replays history and is the wrong transport for Tent managed
   * continuity (Tent is not a transcript UI). Live initialize must advertise
   * sessionCapabilities.resume or this fails loud — no session/new fallback.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error(
        "claude-acp resume requires non-empty provider session id"
      );
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      resumeTransport: "resume",
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const permHooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new AcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      label: "Claude ACP",
      emit: emit2,
      onPermissionAsk: permHooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createClaudeAcpAdapter(options) {
  return new ClaudeAcpProviderAdapter(options);
}

// src/adapters/opencode-acp/types.ts
var OPENCODE_ACP_ADAPTER_ID = "opencode-acp";

// src/adapters/opencode-acp/index.ts
function defaultOpenCodeExecutable() {
  return process.platform === "win32" ? "opencode.exe" : "opencode";
}
var OpenCodeAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = OPENCODE_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.openCodeAcp.displayName";
    this.resolveEnvValue = options.resolveEnvValue ?? ((envKey, planEnv) => resolvePlanOrProcessEnv(envKey, planEnv));
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("external-app");
  }
  resolveLaunch(plan) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (opts.envKey) {
      const value = this.resolveEnvValue(opts.envKey, plan.env);
      if (!value?.trim()) {
        throw new Error(
          `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${opts.envKey}\uFF1Aopencode-acp Agent Connection \u660E\u786E\u8981\u6C42\u8BE5\u503C\uFF08\u4EC5 service \u8FDB\u7A0B / ConnectionLaunchPlan.env\uFF09\u3002`
        );
      }
      env[opts.envKey] = value;
    }
    const command = plan.command?.trim();
    if (!command) {
      throw new Error("Agent Connection is missing canonical command");
    }
    if (!Array.isArray(plan.args)) {
      throw new Error("Agent Connection is missing canonical args");
    }
    return {
      command,
      args: [...plan.args],
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/load (never session/new).
   * Requires agentCapabilities.loadSession on the live initialize handshake.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error(
        "opencode-acp resume requires non-empty provider session id"
      );
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const hooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new AcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      label: "OpenCode ACP",
      emit: emit2,
      onPermissionAsk: hooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createOpenCodeAcpAdapter(options) {
  return new OpenCodeAcpProviderAdapter(options);
}

// src/adapters/copilot-acp/types.ts
var COPILOT_ACP_ADAPTER_ID = "copilot-acp";
var COPILOT_ACP_NPX_PACKAGE = "@github/copilot";

// src/adapters/copilot-acp/index.ts
var CopilotAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = COPILOT_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.copilotAcp.displayName";
    this.resolveEnvValue = options.resolveEnvValue ?? ((envKey, planEnv) => resolvePlanOrProcessEnv(envKey, planEnv));
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("external-app");
  }
  resolveLaunch(plan) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const base = resolveNpxAcpLaunch({
      planCommand: plan.command,
      planArgs: plan.args
    });
    const args = [...base.args];
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (opts.envKey) {
      const value = this.resolveEnvValue(opts.envKey, plan.env);
      if (!value?.trim()) {
        throw new Error(
          `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${opts.envKey}\uFF1Acopilot-acp Agent Connection \u660E\u786E\u8981\u6C42\u8BE5\u503C\uFF08\u4EC5 service \u8FDB\u7A0B / ConnectionLaunchPlan.env\uFF09\u3002\u7701\u7565 envKey \u53EF\u590D\u7528\u672C\u673A Copilot \u767B\u5F55\u3002`
        );
      }
      env[opts.envKey] = value;
    }
    return {
      command: base.command,
      args,
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/load (never session/new).
   * Requires agentCapabilities.loadSession on the live initialize handshake.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error(
        "copilot-acp resume requires non-empty provider session id"
      );
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const hooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new AcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      label: "GitHub Copilot ACP",
      emit: emit2,
      onPermissionAsk: hooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createCopilotAcpAdapter(options) {
  return new CopilotAcpProviderAdapter(options);
}

// src/adapters/pi-acp/types.ts
var PI_ACP_ADAPTER_ID = "pi-acp";
var PI_ACP_NPX_PACKAGE = "pi-acp";

// src/adapters/pi-acp/index.ts
var PiAcpProviderAdapter = class {
  constructor(options = {}) {
    this.id = PI_ACP_ADAPTER_ID;
    this.displayNameKey = "adapter.piAcp.displayName";
    this.resolveEnvValue = options.resolveEnvValue ?? ((envKey, planEnv) => resolvePlanOrProcessEnv(envKey, planEnv));
    this.onPermissionAsk = options.onPermissionAsk;
  }
  capabilities() {
    return loadSessionAcpCapabilities("external-app");
  }
  /**
   * Launch plan validation / env injection only.
   * Real ACP needs bidirectional stdio — AgentRuntime uses startManagedSession.
   */
  resolveLaunch(plan) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const { command, args } = resolveNpxAcpLaunch({
      planCommand: plan.command,
      planArgs: plan.args
    });
    const env = {
      ...plan.env,
      TENT_SESSION_ID: plan.sessionId,
      TENT_CONNECTION_ID: plan.connectionId
    };
    if (opts.envKey) {
      const secret = this.resolveEnvValue(opts.envKey, plan.env);
      if (!secret || !secret.trim()) {
        throw new Error(
          `\u672A\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ${opts.envKey}\uFF1Api-acp Agent Connection \u660E\u786E\u8981\u6C42\u8BE5\u503C\uFF08\u4EC5 service \u8FDB\u7A0B / ConnectionLaunchPlan.env\uFF09\u3002\u7701\u7565 envKey \u53EF\u590D\u7528\u672C\u673A pi \u767B\u5F55/\u914D\u7F6E\u3002`
        );
      }
      env[opts.envKey] = secret;
    }
    return {
      command,
      args,
      cwd: plan.cwd,
      env,
      stopSignal: "SIGTERM"
    };
  }
  async startManagedSession(plan, emit2) {
    const client = this.createClient(plan, emit2);
    return startManagedAcpSession({ plan, emit: emit2, client });
  }
  /**
   * Native ACP resume: new bridge process + session/load (never session/new).
   * Requires agentCapabilities.loadSession on the live initialize handshake.
   */
  async resumeManagedSession(plan, token, emit2) {
    const providerSessionId = (token.providerSessionId ?? token.raw).trim();
    if (!providerSessionId) {
      throw new Error(
        "pi-acp resume requires non-empty provider session id"
      );
    }
    const client = this.createClient(plan, emit2);
    return resumeManagedAcpSession({
      plan,
      emit: emit2,
      client,
      providerSessionId,
      bootstrapPrompt: plan.bootstrapPrompt
    });
  }
  createClient(plan, emit2) {
    const opts = normalizeSharedAcpOpts(readAcpExtras(plan.extras));
    const launch = this.resolveLaunch(plan);
    const sessionProj = readAcpSessionProjection(plan.extras);
    const imageOpts = readBootstrapImageClientOptions(plan);
    const coreChildOpts = readCoreChildEnvClientOptions(plan);
    const hooks = bindAcpPermissionHooks(plan.sessionId, opts.permissionPolicy, {
      onPermissionAsk: this.onPermissionAsk
    });
    return new AcpClient({
      command: launch.command,
      args: launch.args,
      cwd: launch.cwd,
      env: launch.env,
      sessionId: plan.sessionId,
      promptTimeoutMs: opts.promptTimeoutMs,
      permissionPolicy: opts.permissionPolicy,
      mcpServers: sessionProj.mcpServers,
      skills: sessionProj.skills,
      ...imageOpts,
      ...coreChildOpts,
      label: "Pi ACP",
      emit: emit2,
      onPermissionAsk: hooks.onPermissionAsk
    });
  }
  parseResumeToken(raw) {
    return parseAcpResumeToken(raw);
  }
  mapExit(code, signal) {
    return mapAcpProcessExit(code, signal);
  }
};
function createPiAcpAdapter(options) {
  return new PiAcpProviderAdapter(options);
}

// src/service/launch-secret-store.ts
import * as fs13 from "node:fs/promises";
import * as path12 from "node:path";

// src/service/launch-secret-protector.ts
import { spawn as spawn4 } from "node:child_process";
var NON_WINDOWS_MSG = "LaunchSecretStore requires Windows DPAPI (CurrentUser); non-Windows is not supported in this MVP (no weak-crypto fallback)";
function createPlatformLaunchSecretProtector(platform = process.platform) {
  if (platform !== "win32") {
    return {
      protect: async () => {
        throw new Error(NON_WINDOWS_MSG);
      },
      unprotect: async () => {
        throw new Error(NON_WINDOWS_MSG);
      }
    };
  }
  return createWindowsDpapiLaunchSecretProtector();
}
function createWindowsDpapiLaunchSecretProtector() {
  return {
    protect: async (plaintext) => {
      const b64In = Buffer.from(plaintext, "utf8").toString("base64");
      const b64Out = await runPowerShellStdin(
        [
          "Add-Type -AssemblyName System.Security",
          "$b64 = [Console]::In.ReadToEnd().Trim()",
          "$plain = [Convert]::FromBase64String($b64)",
          "$prot = [System.Security.Cryptography.ProtectedData]::Protect($plain, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)",
          "[Convert]::ToBase64String($prot)"
        ].join("; "),
        b64In,
        "protect"
      );
      return b64Out.trim();
    },
    unprotect: async (ciphertext) => {
      const b64Out = await runPowerShellStdin(
        [
          "Add-Type -AssemblyName System.Security",
          "$b64 = [Console]::In.ReadToEnd().Trim()",
          "$prot = [Convert]::FromBase64String($b64)",
          "$plain = [System.Security.Cryptography.ProtectedData]::Unprotect($prot, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)",
          "[Convert]::ToBase64String($plain)"
        ].join("; "),
        ciphertext.trim(),
        "unprotect"
      );
      return Buffer.from(b64Out.trim(), "base64").toString("utf8");
    }
  };
}
function runPowerShellStdin(command, stdinData, op) {
  return new Promise((resolve15, reject) => {
    const child = spawn4(
      "powershell.exe",
      ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", command],
      {
        stdio: ["pipe", "pipe", "pipe"],
        windowsHide: true
      }
    );
    let stdout = "";
    child.stderr?.on("data", () => {
    });
    child.stdout?.on("data", (chunk) => {
      stdout += typeof chunk === "string" ? chunk : chunk.toString("utf8");
    });
    child.on("error", (err) => {
      reject(
        new Error(
          `DPAPI PowerShell ${op} failed to start: ${err instanceof Error ? err.message : "spawn error"}`
        )
      );
    });
    child.on("close", (code) => {
      if (code !== 0) {
        reject(new Error(`DPAPI PowerShell ${op} failed (exit=${code ?? "null"})`));
        return;
      }
      resolve15(stdout.replace(/^\uFEFF/, "").replace(/\r?\n$/, ""));
    });
    child.stdin?.on("error", (err) => {
      reject(
        new Error(
          `DPAPI PowerShell ${op} stdin failed: ${err instanceof Error ? err.message : "stdin error"}`
        )
      );
    });
    child.stdin?.end(stdinData, "utf8");
  });
}

// src/service/launch-secret-store.ts
var LAUNCH_SECRET_ID_RE2 = /^[a-z][a-z0-9-]{0,62}$/;
var MAX_SECRET_BYTES = 64 * 1024;
var MAX_LABEL_LEN = 200;
function launchSecretsPath(dataDir) {
  return path12.join(dataDir, "launch-secrets.json");
}
function assertLaunchSecretId(id) {
  if (typeof id !== "string" || !id.trim()) {
    throw new Error("Missing or invalid launch secret id");
  }
  const trimmed = id.trim();
  if (!LAUNCH_SECRET_ID_RE2.test(trimmed)) {
    throw new Error(
      `Invalid launch secret id: must match ${LAUNCH_SECRET_ID_RE2} (lowercase letter, then a-z0-9-, max 63)`
    );
  }
  return trimmed;
}
function project(rec) {
  const out = {
    id: rec.id,
    createdAt: rec.createdAt,
    updatedAt: rec.updatedAt
  };
  if (rec.metadata?.label) {
    out.label = rec.metadata.label;
    out.metadata = { label: rec.metadata.label };
  }
  return out;
}
function normalizeSetOpts(opts) {
  if (opts === void 0) return void 0;
  if (opts === null) return null;
  if ("metadata" in opts && opts.metadata !== void 0) {
    return normalizeMetadata(opts.metadata);
  }
  if ("label" in opts) {
    if (opts.label === null) return null;
    if (opts.label === void 0) return void 0;
    return normalizeMetadata({ label: opts.label });
  }
  return normalizeMetadata(opts);
}
function normalizeMetadata(raw) {
  if (raw === void 0 || raw === null) return void 0;
  if (typeof raw !== "object" || Array.isArray(raw)) {
    throw new Error("Invalid launch secret metadata: must be a plain object when set");
  }
  const obj = raw;
  const out = {};
  if ("label" in obj) {
    if (obj.label === void 0 || obj.label === null) {
    } else if (typeof obj.label !== "string") {
      throw new Error("Invalid launch secret metadata.label: must be a string");
    } else {
      const t = obj.label.trim();
      if (!t) throw new Error("Invalid launch secret metadata.label: must be non-empty when set");
      if (t.length > MAX_LABEL_LEN) {
        throw new Error(
          `Invalid launch secret metadata.label: exceeds ${MAX_LABEL_LEN} characters`
        );
      }
      out.label = t;
    }
  }
  for (const key2 of Object.keys(obj)) {
    if (key2 !== "label") {
      throw new Error(`Unknown launch secret metadata field: ${key2}`);
    }
  }
  return Object.keys(out).length > 0 ? out : void 0;
}
function isValidDate3(value) {
  return Number.isFinite(Date.parse(value));
}
function parseLaunchSecretRecord(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return null;
  }
  const item = value;
  const allowed = /* @__PURE__ */ new Set(["id", "ciphertext", "createdAt", "updatedAt", "metadata"]);
  if (Object.keys(item).some((key2) => !allowed.has(key2))) return null;
  if (typeof item.id !== "string") return null;
  let id;
  try {
    id = assertLaunchSecretId(item.id);
  } catch {
    return null;
  }
  if (typeof item.ciphertext !== "string" || item.ciphertext.length === 0) {
    return null;
  }
  if (typeof item.createdAt !== "string" || item.createdAt.length === 0) {
    return null;
  }
  if (typeof item.updatedAt !== "string" || item.updatedAt.length === 0) {
    return null;
  }
  if (!isValidDate3(item.createdAt) || !isValidDate3(item.updatedAt)) {
    return null;
  }
  let metadata;
  if (item.metadata !== void 0 && item.metadata !== null) {
    try {
      metadata = normalizeMetadata(item.metadata);
    } catch {
      return null;
    }
  }
  const rec = {
    id,
    ciphertext: item.ciphertext,
    createdAt: item.createdAt,
    updatedAt: item.updatedAt
  };
  if (metadata) rec.metadata = metadata;
  return rec;
}
var LaunchSecretStore = class {
  constructor(dataDir, options) {
    this.records = /* @__PURE__ */ new Map();
    this.loaded = false;
    this.chain = Promise.resolve();
    this.file = launchSecretsPath(dataDir);
    if (options && typeof options === "object" && "protect" in options && "unprotect" in options) {
      this.protector = options;
    } else if (options && typeof options === "object" && "protector" in options) {
      this.protector = options.protector ?? createPlatformLaunchSecretProtector();
    } else {
      this.protector = createPlatformLaunchSecretProtector();
    }
  }
  enqueue(fn) {
    const run = this.chain.then(fn, fn);
    this.chain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async ensureLoaded() {
    if (this.loaded) return;
    return this.enqueue(async () => {
      if (this.loaded) return;
      await this.loadFromDisk();
    });
  }
  async loadFromDisk() {
    try {
      const raw = await fs13.readFile(this.file, "utf8");
      let parsed;
      try {
        parsed = JSON.parse(raw);
      } catch {
        await this.quarantineCorrupt();
        this.loaded = true;
        return;
      }
      if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
        await this.quarantineCorrupt();
        this.loaded = true;
        return;
      }
      const root = parsed;
      if (Object.keys(root).some((key2) => key2 !== "launchSecrets")) {
        await this.quarantineCorrupt();
        this.loaded = true;
        return;
      }
      const list2 = root.launchSecrets;
      if (!Array.isArray(list2)) {
        await this.quarantineCorrupt();
        this.loaded = true;
        return;
      }
      const loaded = /* @__PURE__ */ new Map();
      for (const item of list2) {
        const restored = parseLaunchSecretRecord(item);
        if (!restored) {
          await this.quarantineCorrupt();
          this.loaded = true;
          return;
        }
        loaded.set(restored.id, restored);
      }
      this.records = loaded;
      this.loaded = true;
    } catch (err) {
      if (isNotFoundError(err)) {
        this.loaded = true;
        return;
      }
      throw err;
    }
  }
  async quarantineCorrupt() {
    const backupPath = await backupCorruptMachineFile(this.file);
    warnCorruptMachineState(this.file, backupPath, "reset");
    this.records.clear();
  }
  async persist() {
    const launchSecrets = [...this.records.values()].map((r) => {
      const row = {
        id: r.id,
        ciphertext: r.ciphertext,
        createdAt: r.createdAt,
        updatedAt: r.updatedAt
      };
      if (r.metadata) row.metadata = { ...r.metadata };
      return row;
    }).sort((a, b) => String(a.id).localeCompare(String(b.id)));
    await writeJsonAtomic(this.file, { launchSecrets });
  }
  /**
   * Sync presence after ensureLoaded (projection helper).
   * Call ensureLoaded() first from async handlers when needed.
   */
  has(idRaw) {
    try {
      const id = assertLaunchSecretId(idRaw);
      return this.records.has(id);
    } catch {
      return false;
    }
  }
  async list() {
    await this.ensureLoaded();
    return this.enqueue(
      async () => [...this.records.values()].map(project).sort((a, b) => a.id.localeCompare(b.id))
    );
  }
  /**
   * Store secret under id. Overwrites ciphertext if id exists.
   * Response is id/metadata only — never echoes secret or ciphertext.
   */
  async set(idRaw, secret, opts) {
    const id = assertLaunchSecretId(idRaw);
    if (typeof secret !== "string" || secret.length === 0) {
      throw new Error("launch secret must be a non-empty string");
    }
    if (Buffer.byteLength(secret, "utf8") > MAX_SECRET_BYTES) {
      throw new Error(`launch secret exceeds ${MAX_SECRET_BYTES} bytes`);
    }
    const metaNorm = normalizeSetOpts(opts);
    await this.ensureLoaded();
    return this.enqueue(async () => {
      const ciphertext = await this.protector.protect(secret);
      if (typeof ciphertext !== "string" || !ciphertext.trim()) {
        throw new Error("launch secret protect() returned empty ciphertext");
      }
      if (ciphertext === secret) {
        throw new Error("launch secret protect() must not return plaintext");
      }
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const prev = this.records.get(id);
      const record = {
        id,
        ciphertext: ciphertext.trim(),
        createdAt: prev?.createdAt ?? now,
        updatedAt: now
      };
      if (opts !== void 0) {
        if (metaNorm === null) {
        } else if (metaNorm !== void 0) {
          record.metadata = metaNorm;
        }
      } else if (prev?.metadata) {
        record.metadata = { ...prev.metadata };
      }
      this.records.set(id, record);
      try {
        await this.persist();
      } catch (err) {
        if (prev) this.records.set(id, prev);
        else this.records.delete(id);
        throw err;
      }
      return project(record);
    });
  }
  async delete(idRaw) {
    const id = assertLaunchSecretId(idRaw);
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (!this.records.has(id)) {
        throw new Error(`Launch secret not found: ${id}`);
      }
      const prev = this.records.get(id);
      this.records.delete(id);
      try {
        await this.persist();
      } catch (err) {
        this.records.set(id, prev);
        throw err;
      }
      return { deleted: id };
    });
  }
  /**
   * Service-internal only — returns plaintext for LaunchPlan.env injection.
   * Never exposed as client RPC. Fail-loud when missing.
   */
  async resolve(idRaw) {
    const id = assertLaunchSecretId(idRaw);
    await this.ensureLoaded();
    return this.enqueue(async () => {
      const rec = this.records.get(id);
      if (!rec) {
        throw new Error(`Launch secret not found: ${id}`);
      }
      const plain = await this.protector.unprotect(rec.ciphertext);
      if (typeof plain !== "string" || !plain) {
        throw new Error(`Launch secret unprotect failed for ${id}`);
      }
      return plain;
    });
  }
};

// src/service/connection-field-rules.ts
var ENV_KEY_RE3 = /^[A-Za-z_][A-Za-z0-9_]*$/;
var MAX_TIMEOUT_MS = 24 * 60 * 6e4;
var PERMISSION_POLICIES = /* @__PURE__ */ new Set(["allow", "ask", "deny"]);
var SECRET_CONNECTION_FIELD_HINTS = [
  "apikey",
  "api_key",
  "token",
  "secret",
  "password",
  "authorization",
  "bearer"
];
var ok = (value) => ({ ok: true, value });
var fail = (message2) => ({ ok: false, message: message2 });
function parseConnectionIdValue(raw, field = "connectionId") {
  if (typeof raw !== "string" || !raw.trim()) return fail(`Missing or invalid string param: ${field}`);
  const connectionId = raw.trim();
  return isConnectionId(connectionId) ? ok(connectionId) : fail(`Invalid connectionId: must match ${CONNECTION_ID_RE} (lowercase letter, then a-z0-9-, max 63)`);
}
function parseNonEmptyStringValue(raw, key2) {
  if (typeof raw !== "string" || !raw.trim()) return fail(`Invalid string param: ${key2}`);
  return ok(raw.trim());
}
function parseEnvKeyValue(raw, key2) {
  const value = parseNonEmptyStringValue(raw, key2);
  if (!value.ok) return value;
  return ENV_KEY_RE3.test(value.value) ? value : fail(`Invalid ${key2}: must be a process env name (A-Za-z_ then A-Za-z0-9_)`);
}
function parseEndpointValue(raw) {
  const value = parseNonEmptyStringValue(raw, "endpoint");
  if (!value.ok) return value;
  try {
    const url = new URL(value.value);
    if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
      return fail("Invalid endpoint: only clean absolute http(s) URLs are allowed");
    }
  } catch {
    return fail("Invalid endpoint: must be an absolute http(s) URL");
  }
  return value;
}
function parseLaunchSecretRefValue(raw) {
  const value = parseNonEmptyStringValue(raw, "launchSecretRef");
  if (!value.ok) return value;
  try {
    return ok(assertLaunchSecretId(value.value));
  } catch (err) {
    return fail(
      err instanceof Error ? err.message.replace(/^Invalid launch secret id/, "Invalid launchSecretRef") : `Invalid launchSecretRef: must match ${LAUNCH_SECRET_ID_RE2}`
    );
  }
}
function parsePermissionPolicyValue(raw) {
  return typeof raw === "string" && PERMISSION_POLICIES.has(raw) ? ok(raw) : fail("Invalid permissionPolicy: must be allow|ask|deny");
}
function parsePositiveTimeoutValue(raw, key2) {
  return typeof raw === "number" && Number.isInteger(raw) && raw > 0 && raw <= MAX_TIMEOUT_MS ? ok(raw) : fail(`Invalid ${key2}: must be a positive integer no greater than ${MAX_TIMEOUT_MS}`);
}

// src/service/connections.ts
var AGENT_CONNECTION_CREATE_FIELDS = [
  "connectionId",
  "provider",
  "adapterId",
  "displayName",
  "command",
  "args",
  "model",
  "envKey",
  "launchSecretRef",
  "endpoint",
  "permissionPolicy",
  "promptTimeoutMs",
  "permissionTimeoutMs",
  "skills",
  "mcpServers"
];
var AGENT_CONNECTION_UPDATE_FIELDS = AGENT_CONNECTION_CREATE_FIELDS.filter((field) => field !== "connectionId");
var DISK_KEYS = new Set(AGENT_CONNECTION_CREATE_FIELDS);
function connectionsPath(dataDir) {
  return path13.join(dataDir, "connections.json");
}
function diskOptional(value, parse2) {
  if (value === void 0 || value === null) return void 0;
  const result = parse2(value);
  return result.ok ? result.value : null;
}
function parseArgsValue(value) {
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
    return { ok: false, message: "Invalid args: must be an array of strings" };
  }
  return { ok: true, value: [...value] };
}
function parseConnectionRow(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
  const row = value;
  if (Object.keys(row).some((key2) => !DISK_KEYS.has(key2))) return null;
  const connectionId = parseConnectionIdValue(row.connectionId);
  const provider = parseNonEmptyStringValue(row.provider, "provider");
  const adapterId = parseNonEmptyStringValue(row.adapterId, "adapterId");
  if (!connectionId.ok || !provider.ok || !adapterId.ok) return null;
  const route = { connectionId: connectionId.value, provider: provider.value, adapterId: adapterId.value };
  const optional2 = (key2, parse2, assign) => {
    const parsed = diskOptional(row[key2], parse2);
    if (parsed === null) return false;
    if (parsed !== void 0) assign(parsed);
    return true;
  };
  if (!optional2("displayName", (v) => parseNonEmptyStringValue(v, "displayName"), (v) => {
    route.displayName = v;
  })) return null;
  if (!optional2("command", (v) => parseNonEmptyStringValue(v, "command"), (v) => {
    route.command = v;
  })) return null;
  if (!optional2("args", parseArgsValue, (v) => {
    route.args = v;
  })) return null;
  if (!optional2("model", (v) => parseNonEmptyStringValue(v, "model"), (v) => {
    route.model = v;
  })) return null;
  if (!optional2("envKey", (v) => parseEnvKeyValue(v, "envKey"), (v) => {
    route.envKey = v;
  })) return null;
  if (!optional2("launchSecretRef", parseLaunchSecretRefValue, (v) => {
    route.launchSecretRef = v;
  })) return null;
  if (!optional2("endpoint", parseEndpointValue, (v) => {
    route.endpoint = v;
  })) return null;
  if (!optional2("permissionPolicy", parsePermissionPolicyValue, (v) => {
    route.permissionPolicy = v;
  })) return null;
  if (!optional2("promptTimeoutMs", (v) => parsePositiveTimeoutValue(v, "promptTimeoutMs"), (v) => {
    route.promptTimeoutMs = v;
  })) return null;
  if (!optional2("permissionTimeoutMs", (v) => parsePositiveTimeoutValue(v, "permissionTimeoutMs"), (v) => {
    route.permissionTimeoutMs = v;
  })) return null;
  if (row.skills !== void 0 && row.skills !== null) {
    const skills = parseSkillsArrayValue(row.skills, defaultAllowedSkillRoots());
    if (!skills.ok) return null;
    route.skills = skills.value;
  }
  if (row.mcpServers !== void 0 && row.mcpServers !== null) {
    const mcp = parseMcpServersArrayValue(row.mcpServers);
    if (!mcp.ok) return null;
    route.mcpServers = mcp.value;
  }
  if (!route.command || !Array.isArray(route.args)) return null;
  return route;
}
function unreadableConnections(file) {
  throw new Error(
    truncateUtf8Text(`Agent Connections are unreadable: ${file}`, 2048)
  );
}
async function loadAgentConnections(dataDir) {
  const file = connectionsPath(dataDir);
  let text3;
  try {
    text3 = await fs14.readFile(file, "utf8");
  } catch (err) {
    if (isNotFoundError(err)) return [];
    return unreadableConnections(file);
  }
  let parsed;
  try {
    parsed = JSON.parse(text3);
  } catch {
    return unreadableConnections(file);
  }
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
    return unreadableConnections(file);
  }
  const connections = parsed.connections;
  if (!Array.isArray(connections)) return unreadableConnections(file);
  const out = [];
  for (const value of connections) {
    const connection = parseConnectionRow(value);
    if (!connection || out.some((existing) => existing.connectionId === connection.connectionId)) {
      return unreadableConnections(file);
    }
    out.push(connection);
  }
  return out;
}
async function saveAgentConnections(dataDir, connections) {
  await writeJsonAtomic(connectionsPath(dataDir), {
    connections: connections.map(cloneAgentConnection)
  });
}
function npxLaunch(packageSpec, trailing = []) {
  const launch = defaultNpxLaunch();
  return { command: launch.command, args: [...launch.argsPrefix, "--yes", packageSpec, ...trailing] };
}
function grokArgs(model, endpoint) {
  return [
    "agent",
    "--model",
    model,
    "--no-leader",
    ...endpoint ? ["--cli-chat-proxy-base-url", endpoint, "--xai-api-base-url", endpoint] : [],
    "stdio"
  ];
}
function materializeAgentConnectionLaunch(connection) {
  if (connection.command) {
    return { ...connection, args: [...connection.args ?? []] };
  }
  switch (connection.adapterId) {
    case GROK_ACP_ADAPTER_ID:
      return {
        ...connection,
        command: defaultGrokExecutable(),
        args: grokArgs(connection.model ?? DEFAULT_GROK_MODEL, connection.endpoint)
      };
    case CODEX_ACP_ADAPTER_ID:
      return { ...connection, ...npxLaunch(CODEX_ACP_NPX_PACKAGE) };
    case CLAUDE_ACP_ADAPTER_ID:
      return { ...connection, ...npxLaunch(CLAUDE_ACP_NPX_PACKAGE) };
    case COPILOT_ACP_ADAPTER_ID:
      return {
        ...connection,
        ...npxLaunch(COPILOT_ACP_NPX_PACKAGE, [
          "--acp",
          "--stdio",
          ...connection.model ? ["--model", connection.model] : []
        ])
      };
    case PI_ACP_ADAPTER_ID:
      return { ...connection, ...npxLaunch(PI_ACP_NPX_PACKAGE) };
    case OPENCODE_ACP_ADAPTER_ID:
      return { ...connection, command: defaultOpenCodeExecutable(), args: ["acp"] };
    default:
      throw new Error(`Agent Connection ${connection.connectionId} requires canonical command and args`);
  }
}
function withAgentConnectionEndpoint(connection, endpoint) {
  const next = { ...connection, ...endpoint ? { endpoint } : {} };
  if (!endpoint) delete next.endpoint;
  if (connection.adapterId !== GROK_ACP_ADAPTER_ID || !Array.isArray(connection.args)) {
    return next;
  }
  const args = [];
  for (let i = 0; i < connection.args.length; i += 1) {
    const value = connection.args[i];
    if (value === "--cli-chat-proxy-base-url" || value === "--xai-api-base-url") {
      i += 1;
      continue;
    }
    args.push(value);
  }
  const stdio = args.lastIndexOf("stdio");
  if (endpoint && stdio >= 0) {
    args.splice(stdio, 0, "--cli-chat-proxy-base-url", endpoint, "--xai-api-base-url", endpoint);
  }
  return { ...next, args };
}
function defaultAgentConnections(env = process.env) {
  const route = (connectionId, provider, adapterId, extra = {}) => ({
    connectionId,
    provider,
    adapterId,
    permissionPolicy: "deny",
    ...extra
  });
  return [
    materializeAgentConnectionLaunch(route("grok-acp-default", "grok", GROK_ACP_ADAPTER_ID, {
      model: DEFAULT_GROK_MODEL,
      envKey: DEFAULT_GROK_ENV_KEY,
      endpoint: env[DEFAULT_GROK_BASE_URL_ENV_KEY]?.trim() || void 0
    })),
    materializeAgentConnectionLaunch(route("codex-acp-default", "codex", CODEX_ACP_ADAPTER_ID)),
    materializeAgentConnectionLaunch(route("claude-acp-default", "claude", CLAUDE_ACP_ADAPTER_ID)),
    materializeAgentConnectionLaunch(route("opencode-acp-default", "opencode", OPENCODE_ACP_ADAPTER_ID)),
    materializeAgentConnectionLaunch(route("copilot-acp-default", "copilot", COPILOT_ACP_ADAPTER_ID)),
    materializeAgentConnectionLaunch(route("pi-acp-default", "pi", PI_ACP_ADAPTER_ID))
  ];
}
async function ensureDefaultAgentConnections(dataDir) {
  const file = connectionsPath(dataDir);
  try {
    await fs14.access(file);
    return loadAgentConnections(dataDir);
  } catch (err) {
    if (!isNotFoundError(err)) throw err;
  }
  const defaults = defaultAgentConnections();
  await saveAgentConnections(dataDir, defaults);
  return defaults;
}
function projectAgentConnection(connection, opts) {
  const launchSecretRef = connection.launchSecretRef?.trim() || void 0;
  const skills = projectSkillRefs(connection.skills);
  const mcpServers = projectMcpServers(connection.mcpServers);
  return {
    connectionId: connection.connectionId,
    provider: connection.provider,
    adapterId: connection.adapterId,
    displayName: connection.displayName?.trim() || connection.connectionId,
    ...connection.command ? { command: connection.command } : {},
    ...connection.args ? { args: [...connection.args] } : {},
    ...connection.model ? { model: connection.model } : {},
    ...connection.envKey ? { envKey: connection.envKey } : {},
    ...launchSecretRef ? { launchSecretRef } : {},
    ...launchSecretRef && opts?.launchSecretExists !== void 0 ? { launchSecretExists: opts.launchSecretExists } : {},
    ...connection.endpoint ? { endpoint: connection.endpoint } : {},
    ...connection.permissionPolicy ? { permissionPolicy: connection.permissionPolicy } : {},
    ...connection.promptTimeoutMs !== void 0 ? { promptTimeoutMs: connection.promptTimeoutMs } : {},
    ...connection.permissionTimeoutMs !== void 0 ? { permissionTimeoutMs: connection.permissionTimeoutMs } : {},
    ...skills ? {
      skills,
      skillsProjectionMode: "metadata-provider-dependent",
      skillsNote: "Skill name/path refs only (_meta.tent.skills). Provider-dependent; not a claim of activation."
    } : {},
    ...mcpServers ? { mcpServers } : {}
  };
}
function projectAgentConnections(connections, opts) {
  const lookup = (ref) => {
    if (!ref || !opts?.launchSecretExistsById) return void 0;
    return opts.launchSecretExistsById instanceof Map ? opts.launchSecretExistsById.get(ref) : opts.launchSecretExistsById[ref];
  };
  return connections.map((connection) => {
    const exists = lookup(connection.launchSecretRef?.trim());
    return projectAgentConnection(
      connection,
      exists === void 0 ? void 0 : { launchSecretExists: exists }
    );
  }).sort((a, b) => a.connectionId.localeCompare(b.connectionId));
}

// src/service/provider-catalog.ts
var PRODUCT_ACP_ADAPTER_IDS = [
  "grok-acp",
  "codex-acp",
  "claude-acp",
  "opencode-acp",
  "copilot-acp",
  "pi-acp"
];
var PROVIDER_VERIFICATION_LEVELS_BY_ADAPTER = {
  // Checked-in opt-in: npm run test:grok-e2e (+ also in test:foreground-e2e).
  "grok-acp": "opt-in-live-probe",
  // Checked-in opt-in: npm run test:foreground-e2e with TENT_LIVE_PROVIDERS=…
  "codex-acp": "opt-in-live-probe",
  "claude-acp": "opt-in-live-probe",
  "opencode-acp": "opt-in-live-probe",
  "copilot-acp": "opt-in-live-probe",
  // Mock suite + initialize/session-new probe evidence; no checked-in paid live E2E yet.
  "pi-acp": "mock-tested"
};
var NATIVE_FOREGROUND_BY_ADAPTER = {
  "grok-acp": "verified",
  "codex-acp": "verified",
  "claude-acp": "verified",
  "opencode-acp": "verified",
  "copilot-acp": "verified",
  // pi-acp maps loadSession + session-map to pi session files; native CLI
  // hand-back not covered by Tent's foreground-e2e matrix yet.
  "pi-acp": "unverified"
};
var PROVIDER_CATALOG_NOTES = {
  "grok-acp": "mock suite + opt-in live E2E (test:grok-e2e); not automatic CI certification",
  "codex-acp": "mock suite + opt-in live probe (test:foreground-e2e); not automatic CI certification",
  "claude-acp": "mock suite + opt-in live probe (test:foreground-e2e); Node bridge may require \u226522",
  "opencode-acp": "mock suite + opt-in live probe (test:foreground-e2e); not automatic CI certification",
  "copilot-acp": "mock suite + opt-in live probe (test:foreground-e2e); not automatic CI certification",
  "pi-acp": "third-party pi-acp bridge; mock suite; initialize loadSession verified when pi is installed; no paid live E2E in default CI"
};
var LEVEL_SET = new Set(PROVIDER_VERIFICATION_LEVELS);
function defaultProductAcpAdapters() {
  return [
    createGrokAcpAdapter(),
    createCodexAcpAdapter(),
    createClaudeAcpAdapter(),
    createOpenCodeAcpAdapter(),
    createCopilotAcpAdapter(),
    createPiAcpAdapter()
  ];
}
function projectProviderCatalog() {
  const byId = new Map(
    defaultProductAcpAdapters().map((adapter) => [adapter.id, adapter])
  );
  const providers = [];
  for (const adapterId of PRODUCT_ACP_ADAPTER_IDS) {
    const adapter = byId.get(adapterId);
    if (!adapter) {
      throw new Error(
        `provider catalog missing product adapter instance for adapterId: ${adapterId}`
      );
    }
    const verificationLevel = PROVIDER_VERIFICATION_LEVELS_BY_ADAPTER[adapterId];
    if (!verificationLevel) {
      throw new Error(
        `provider catalog missing verification level for product adapterId: ${adapterId}`
      );
    }
    const notes = PROVIDER_CATALOG_NOTES[adapterId];
    providers.push({
      adapterId,
      verificationLevel,
      canResume: adapter.capabilities().canResume === true,
      nativeForeground: NATIVE_FOREGROUND_BY_ADAPTER[adapterId],
      ...notes ? { notes } : {}
    });
  }
  return { providers };
}

// src/service/workspace-admin-handlers.ts
async function handleWorkspaceSettings(deps, params) {
  const workspaceId = deps.requireWorkspaceId(params);
  const mount = deps.host.require(workspaceId);
  const settings = await loadWorkspaceSettings(mount.env.fs);
  return {
    workspaceId,
    settings: projectWorkspaceSettings(settings)
  };
}
async function handleWorkspaceSettingsUpdate(deps, params) {
  deps.requireUserActor(params, "workspace.settings.update");
  const workspaceId = deps.requireWorkspaceId(params);
  const mount = deps.host.require(workspaceId);
  const patch = parseWorkspaceSettingsPatch(params);
  return deps.mutations.run(workspaceId, async () => {
    deps.host.markSelfWrite(workspaceId);
    let result;
    try {
      result = await updateWorkspaceSettings(mount.env.fs, patch);
    } catch (err) {
      if (err instanceof WorkspaceSettingsError || err instanceof Error && err.name === "WorkspaceSettingsError") {
        const code = err instanceof WorkspaceSettingsError ? err.code : err.code ?? "INVALID_PATCH";
        throw new RpcError(-32602, err.message, { code });
      }
      throw err;
    }
    if (result.changed) {
      emitWorkspaceSettingsUpdated(deps, workspaceId, result.settings);
    }
    return {
      workspaceId,
      settings: projectWorkspaceSettings(result.settings),
      changed: result.changed
    };
  });
}
async function handleWorkspaceAgents(deps, params) {
  const workspaceId = deps.requireWorkspaceId(params);
  const mount = deps.host.require(workspaceId);
  const file = await loadWorkspaceAgents(mount.workspaceRoot);
  return projectWorkspaceAgents(workspaceId, file);
}
async function handleWorkspaceAgentsWrite(deps, params) {
  deps.requireUserActor(params, "workspace.agents.write");
  const workspaceId = deps.requireWorkspaceId(params);
  const mount = deps.host.require(workspaceId);
  if (typeof params.content !== "string") {
    throw new RpcError(-32602, "workspace.agents.write requires string content");
  }
  const content3 = params.content;
  const baseEtag = deps.optionalString(params, "baseEtag");
  return deps.mutations.run(workspaceId, async () => {
    const before = await loadWorkspaceAgents(mount.workspaceRoot);
    const currentEtag = contentEtag(before.content);
    if (baseEtag && baseEtag !== currentEtag) {
      throw new RpcError(-32009, "etag conflict", {
        currentEtag,
        baseEtag,
        path: WORKSPACE_AGENTS_FILENAME
      });
    }
    deps.host.markSelfWrite(workspaceId);
    let result;
    try {
      result = await writeWorkspaceAgents(mount.workspaceRoot, content3);
    } catch (err) {
      if (err instanceof WorkspaceAgentsError || err instanceof Error && err.name === "WorkspaceAgentsError") {
        const code = err instanceof WorkspaceAgentsError ? err.code : err.code ?? "INVALID_CONTENT";
        throw new RpcError(-32602, err.message, { code });
      }
      throw err;
    }
    const projection = projectWorkspaceAgents(workspaceId, result.file);
    if (result.changed) {
      deps.events.emit(
        "workspace.agents.updated",
        workspaceId,
        {
          path: projection.path,
          content: projection.content,
          exists: projection.exists,
          etag: projection.etag
        },
        "self"
      );
    }
    return {
      ...projection,
      changed: result.changed
    };
  });
}
function parseWorkspaceSettingsPatch(params) {
  if (typeof params.patch === "object" && params.patch !== null && !Array.isArray(params.patch)) {
    throw new RpcError(
      -32602,
      "workspace.settings.update does not accept nested patch; pass fields at the top level"
    );
  }
  const reserved = /* @__PURE__ */ new Set(["workspaceId", "actor", "patch"]);
  const supported = /* @__PURE__ */ new Set(["defaultAcceptMode"]);
  const out = {};
  for (const [key2, value] of Object.entries(params)) {
    if (reserved.has(key2)) continue;
    if (!supported.has(key2)) {
      throw new RpcError(-32602, `Unknown workspace setting: ${key2}`);
    }
    if (value === void 0) continue;
    out[key2] = value;
  }
  if ("defaultAcceptMode" in out) {
    const value = out.defaultAcceptMode;
    if (value !== "review-required" && value !== "auto-accept" && value !== "agent-decide") {
      throw new RpcError(-32602, `Invalid defaultAcceptMode: ${String(value)}`, {
        code: "INVALID_ACCEPT_MODE"
      });
    }
  }
  return out;
}
function projectWorkspaceSettings(settings) {
  return { ...settings };
}
function emitWorkspaceSettingsUpdated(deps, workspaceId, settings) {
  deps.events.emit(
    "workspace.settings.updated",
    workspaceId,
    { settings: projectWorkspaceSettings(settings) },
    "self"
  );
}
function projectWorkspaceAgents(workspaceId, file) {
  return {
    workspaceId,
    path: file.path,
    content: file.content,
    exists: file.exists,
    etag: contentEtag(file.content)
  };
}

// src/machine/skills.ts
import * as fs15 from "node:fs/promises";
import * as os4 from "node:os";
import * as path14 from "node:path";
var SKILL_TARGET_IDS = ["shared-agents", "claude"];
var SAFE_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
function isSkillTargetId(value) {
  return SKILL_TARGET_IDS.includes(value);
}
function skillTargetDir(target, home) {
  const root = home ?? os4.homedir();
  switch (target) {
    case "claude":
      return path14.join(root, ".claude", "skills");
    case "shared-agents":
      return path14.join(root, ".agents", "skills");
    default: {
      const _exhaustive = target;
      throw new Error(`Unknown skill target: ${String(_exhaustive)}`);
    }
  }
}
function assertSafeSkillName2(name) {
  const trimmed = name.trim();
  if (!trimmed || !SAFE_SKILL_NAME.test(trimmed) || trimmed.includes("..") || trimmed.includes("/") || trimmed.includes("\\") || path14.basename(trimmed) !== trimmed) {
    throw new Error(`Invalid skill name: ${name}`);
  }
  return trimmed;
}
function parseSkillTargetId(value) {
  const trimmed = value.trim();
  if (!isSkillTargetId(trimmed)) {
    throw new Error(
      `Unknown skill target: ${value} (allowed: ${SKILL_TARGET_IDS.join(", ")})`
    );
  }
  return trimmed;
}
function bundledSkillsDir(packageRoot) {
  return path14.join(packageRoot, "skills");
}
async function listBundledSkillNames(packageRoot) {
  const sourceDir = bundledSkillsDir(packageRoot);
  let entries;
  try {
    entries = await fs15.readdir(sourceDir, { withFileTypes: true });
  } catch (err) {
    const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
    if (code === "ENOENT") {
      throw new Error(`No installable skills found in ${sourceDir}`);
    }
    throw err;
  }
  const skillNames = [];
  for (const entry of entries) {
    if (!entry.isDirectory()) continue;
    if (!SAFE_SKILL_NAME.test(entry.name)) continue;
    if (await existsPath(path14.join(sourceDir, entry.name, "SKILL.md"))) {
      skillNames.push(entry.name);
    }
  }
  skillNames.sort();
  return skillNames;
}
async function listSkills(options) {
  const home = options.home ?? os4.homedir();
  const names = await listBundledSkillNames(options.packageRoot);
  const skills = [];
  for (const name of names) {
    const targets = [];
    for (const target of SKILL_TARGET_IDS) {
      const dir = skillTargetDir(target, home);
      const skillPath = path14.join(dir, name);
      assertChildPath(dir, skillPath);
      targets.push({
        target,
        path: skillPath,
        installed: await existsPath(skillPath)
      });
    }
    skills.push({ name, targets });
  }
  return { skills };
}
async function installSkills(options) {
  const home = options.home ?? os4.homedir();
  const force = options.force === true;
  const sourceDir = bundledSkillsDir(options.packageRoot);
  const allNames = await listBundledSkillNames(options.packageRoot);
  if (allNames.length === 0) {
    throw new Error(`No installable skills found in ${sourceDir}`);
  }
  const selectedNames = resolveSkillSelection(options.skills, allNames);
  const destinations = resolveInstallDestinations(options, home);
  if (destinations.length === 0) {
    throw new Error("skill-install requires at least one target directory");
  }
  const results = [];
  for (const dest of destinations) {
    await fs15.mkdir(dest.dir, { recursive: true });
    for (const name of selectedNames) {
      const source = path14.join(sourceDir, name);
      const target = path14.join(dest.dir, name);
      assertChildPath(sourceDir, source);
      assertChildPath(dest.dir, target);
      const exists = await existsPath(target);
      if (exists && !force) {
        results.push({
          targetDir: dest.dir,
          ...dest.target ? { target: dest.target } : {},
          skill: name,
          status: "skipped",
          reason: "already exists (use --force to overwrite)"
        });
        continue;
      }
      if (exists && force) {
        await fs15.rm(target, { recursive: true, force: true });
      }
      await fs15.cp(source, target, { recursive: true, errorOnExist: true });
      results.push({
        targetDir: dest.dir,
        ...dest.target ? { target: dest.target } : {},
        skill: name,
        status: "installed"
      });
    }
  }
  return results;
}
function resolveSkillSelection(requested, allNames) {
  if (!requested || requested.length === 0) return [...allNames];
  const known = new Set(allNames);
  const selected = [];
  const seen = /* @__PURE__ */ new Set();
  for (const raw of requested) {
    const name = assertSafeSkillName2(raw);
    if (!known.has(name)) {
      throw new Error(`Unknown bundled skill: ${name}`);
    }
    if (seen.has(name)) continue;
    seen.add(name);
    selected.push(name);
  }
  selected.sort();
  return selected;
}
function resolveInstallDestinations(options, home) {
  if (options.targetDirs !== void 0) {
    if (options.targetDirs.length === 0) {
      throw new Error("skill-install requires at least one target directory");
    }
    return options.targetDirs.map((dir) => ({ dir: path14.resolve(dir) }));
  }
  const targetIds = options.targets && options.targets.length > 0 ? options.targets.map((t) => parseSkillTargetId(t)) : [...SKILL_TARGET_IDS];
  const seen = /* @__PURE__ */ new Set();
  const out = [];
  for (const id of targetIds) {
    if (seen.has(id)) continue;
    seen.add(id);
    out.push({ dir: skillTargetDir(id, home), target: id });
  }
  return out;
}
function assertChildPath(parent, child) {
  const rel = path14.relative(path14.resolve(parent), path14.resolve(child));
  if (rel.startsWith("..") || path14.isAbsolute(rel)) {
    throw new Error(`Install target escapes the destination directory: ${child}`);
  }
}
async function existsPath(target) {
  try {
    await fs15.access(target);
    return true;
  } catch {
    return false;
  }
}

// src/service/handlers.ts
function makeWorkspaceAdminDeps(ctx) {
  return {
    host: ctx.host,
    mutations: ctx.mutations,
    events: ctx.events,
    requireWorkspaceId: (params) => requireWorkspaceId(ctx, params),
    requireUserActor,
    optionalString: optionalString2
  };
}
function isTaskLifecycleErrorLike(error) {
  if (error instanceof TaskLifecycleError) {
    return true;
  }
  if (!(error instanceof Error)) return false;
  if (error.name === "TaskLifecycleError") {
    const code = error.code;
    return typeof code === "string" && code.length > 0;
  }
  if (/^Invalid task transition:/.test(error.message)) {
    const code = error.code;
    if (typeof code === "string" && code.length > 0) return true;
    error.code = "INVALID_TRANSITION";
    return true;
  }
  return false;
}
async function dispatchMethod(ctx, method, params, callContext = {}) {
  if (method.startsWith("AgentRuntimePort.") || method.startsWith("AgentRuntime.")) {
    throw new RpcError(
      -32601,
      `Method not found (AgentRuntimePort is service-internal): ${method}`
    );
  }
  if (!isClientMethod(method)) {
    throw new RpcError(-32601, `Method not found: ${method}`);
  }
  const p = params ?? {};
  try {
    switch (method) {
      case "service.health":
        return health(ctx);
      case "service.subscribe":
        return { ok: true, transport: "sse", path: "/events" };
      case "workspace.mount":
        return workspaceMount(ctx, p);
      case "workspace.unmount":
        return workspaceUnmount(ctx, p);
      case "workspace.list":
        return { workspaces: ctx.host.list() };
      case "workspace.setForeground":
        return workspaceSetForeground(ctx, p);
      case "workspace.collaboration":
        return workspaceCollaborationRpc(ctx, p);
      case "workspace.settings":
        return handleWorkspaceSettings(makeWorkspaceAdminDeps(ctx), p);
      case "workspace.settings.update":
        return handleWorkspaceSettingsUpdate(makeWorkspaceAdminDeps(ctx), p);
      case "workspace.agents":
        return handleWorkspaceAgents(makeWorkspaceAdminDeps(ctx), p);
      case "workspace.agents.write":
        return handleWorkspaceAgentsWrite(makeWorkspaceAdminDeps(ctx), p);
      case "docs.list":
        return docsList(ctx, p);
      case "docs.get":
        return docsGet(ctx, p);
      case "docs.readForEdit":
        return docsReadForEdit(ctx, p);
      case "docs.write":
        return docsWrite(ctx, p);
      case "docs.createNote":
        return docsCreateNote(ctx, p);
      case "docs.fork":
        return docsFork(ctx, p);
      case "docs.rename":
        return docsRename(ctx, p);
      case "docs.move":
        return docsMove(ctx, p);
      case "docs.setMode":
        return docsSetMode(ctx, p);
      case "docs.search":
        return docsSearch(ctx, p);
      case "docs.backlinks":
        return docsBacklinks(ctx, p);
      case "docs.importAttachment":
        return docsImportAttachment(ctx, p);
      case "docs.setType":
        return docsSetType(ctx, p);
      case "docs.tags.set":
        return docsTagsSet(ctx, p);
      case "docs.tag.add":
        return docsTagAdd(ctx, p);
      case "docs.tag.remove":
        return docsTagRemove(ctx, p);
      case "relation.list":
        return relationList(ctx, p);
      case "relation.create":
        return relationCreate(ctx, p);
      case "relation.update":
        return relationUpdate(ctx, p);
      case "relation.delete":
        return relationDelete(ctx, p);
      case "registry.tags":
        return registryTags(ctx, p);
      case "registry.tag.create":
        return registryTagCreate(ctx, p);
      case "registry.tag.delete":
        return registryTagDelete(ctx, p);
      case "registry.roles":
        return registryRoles(ctx, p);
      case "registry.role.create":
        return registryRoleCreate(ctx, p);
      case "registry.role.update":
        return registryRoleUpdate(ctx, p);
      case "registry.role.delete":
        return registryRoleDelete(ctx, p);
      case "connection.list":
        return connectionList(ctx, p);
      case "connection.get":
        return connectionGet(ctx, p);
      case "connection.create":
        return connectionCreate(ctx, p);
      case "connection.update":
        return connectionUpdate(ctx, p);
      case "connection.delete":
        return connectionDelete(ctx, p);
      case "provider.catalog":
        return providerCatalogRpc();
      case "settings.launchSecret.list":
        return settingsLaunchSecretList(ctx, p, callContext);
      case "settings.launchSecret.set":
        return settingsLaunchSecretSet(ctx, p, callContext);
      case "settings.launchSecret.delete":
        return settingsLaunchSecretDelete(ctx, p, callContext);
      case "skill.list":
        return skillList(ctx);
      case "skill.install":
        return skillInstall(ctx, p);
      case "task.dispatch":
        return taskDispatch(ctx, p, callContext);
      case "task.claim":
        return taskClaimRpc(ctx, p, {
          sessionId: callContext.callerSessionId,
          externalKey: callContext.callerExternalKey
        });
      case "task.claimDirect":
        return taskClaimDirectRpc(ctx, p, {
          sessionId: callContext.callerSessionId,
          externalKey: callContext.callerExternalKey
        });
      case "task.wait":
        return taskWaitRpc(ctx, p);
      case "task.resume":
        return taskResumeRpc(ctx, p);
      case "task.requestDecision":
        return taskRequestDecisionRpc(ctx, p, {
          callerSessionId: callContext.callerSessionId
        });
      case "task.sendInput":
        return taskSendInputRpc(ctx, p);
      case "task.submit":
        return taskSubmitRpc(ctx, p, callContext);
      case "task.accept":
        return taskAcceptRpc(ctx, p, callContext);
      case "task.reject":
        return taskRejectRpc(ctx, p, callContext);
      case "task.interrupt":
        return taskInterruptRpc(ctx, p);
      case "task.cancel":
        return taskCancelRpc(ctx, p);
      case "task.startSession":
        return taskStartSessionRpc(ctx, p);
      case "task.replaceSession":
        return taskReplaceSessionRpc(ctx, p);
      case "task.list":
        return taskList(ctx, p);
      case "task.get":
        return taskGet(ctx, p);
      case "task.package":
        return taskPackage(ctx, p);
      case "task.bindOutput":
        return taskBindOutputRpc(ctx, p, callContext);
      case "taskResult.list":
        return taskResultList(ctx, p);
      case "taskResult.get":
        return taskResultGet(ctx, p);
      case "output.provenance":
        return outputProvenanceRpc(ctx, p);
      case "graph.projection":
        return graphProjectionRpc(ctx, p);
      case "proposal.list":
        return proposalList(ctx, p);
      case "proposal.submit":
        return proposalSubmit(ctx, p);
      case "proposal.resolve":
        return proposalResolve(ctx, p);
      case "session.list":
        return sessionList(ctx, p);
      case "session.get":
        return sessionGet(ctx, p);
      case "session.enter":
        return sessionEnter(ctx, p);
      case "session.status":
        return sessionStatus(ctx, p);
      case "session.leave":
        return sessionLeave(ctx, p);
      case "toolApproval.listPending":
        return toolApprovalListPending(ctx, p);
      case "toolApproval.get":
        return toolApprovalGet(ctx, p);
      case "toolApproval.approveOnce":
        return toolApprovalResolve(ctx, p, "approved");
      case "toolApproval.deny":
        return toolApprovalResolve(ctx, p, "denied");
      case "decisionRequest.listPending":
        return decisionRequestListPending(ctx, p, {
          callerSessionId: callContext.callerSessionId
        });
      case "decisionRequest.get":
        return decisionRequestGet(ctx, p, {
          callerSessionId: callContext.callerSessionId
        });
      case "decisionRequest.respond":
        return decisionRequestRespondRpc(ctx, p, {
          callerSessionId: callContext.callerSessionId
        });
      case "decisionRequest.escalate":
        return decisionRequestEscalateRpc(ctx, p, {
          callerSessionId: callContext.callerSessionId
        });
      case "interaction.listPending":
        return interactionListPending(ctx, p);
      case "taskInput.listPending":
        return taskInputListPending(ctx, p);
      case "taskInput.get":
        return taskInputGet(ctx, p);
      case "taskInput.ack":
        return taskInputAckRpc(ctx, p);
      case "operationalRetention.preview":
        return operationalRetentionPreviewRpc(ctx, p);
      case "operationalRetention.purge":
        return operationalRetentionPurgeRpc(ctx, p);
      case "task.worktreeReclaim.preview":
        return taskWorktreeReclaimPreviewRpc(ctx, p);
      case "task.worktreeReclaim.reconcile":
        return taskWorktreeReclaimReconcileRpc(ctx, p, callContext);
      case "annotation.list":
        return annotationListRpc(ctx, p);
      case "annotation.create":
        return annotationCreateRpc(ctx, p);
      case "annotation.resolve":
        return annotationResolveRpc(ctx, p);
      case "annotation.reopen":
        return annotationReopenRpc(ctx, p);
      case "annotation.delete":
        return annotationDeleteRpc(ctx, p);
      default:
        throw new RpcError(-32601, `Method not found: ${method}`);
    }
  } catch (error) {
    if (error instanceof RpcError) throw error;
    if (error instanceof AnnotationError) {
      throw annotationErrorToRpc(error);
    }
    if (error instanceof RetentionError || error instanceof Error && error.name === "RetentionError") {
      const code = error instanceof RetentionError ? error.code : error.code ?? "INVALID_KEEP_DAYS";
      throw new RpcError(-32602, error.message, { code });
    }
    if (error instanceof WorkspaceSettingsError || error instanceof Error && error.name === "WorkspaceSettingsError") {
      const code = error instanceof WorkspaceSettingsError ? error.code : error.code ?? "INVALID_PATCH";
      throw new RpcError(-32602, error.message, { code });
    }
    if (isTaskLifecycleErrorLike(error)) {
      throw new RpcError(RPC_LIFECYCLE, error.message, { code: error.code });
    }
    throw error;
  }
}
function health(ctx) {
  return {
    status: "ok",
    instanceId: ctx.instanceId,
    pid: ctx.getPid(),
    version: ctx.version,
    /** Wire protocol contract — independent of package version (0.2.0). */
    protocolVersion: ctx.protocolVersion,
    startedAt: ctx.startedAt,
    workspaceCount: ctx.host.list().length,
    foregroundWorkspaceId: ctx.host.getForegroundId()
  };
}
async function workspaceMount(ctx, p) {
  const workspaceRoot = requireString(p, "workspaceRoot");
  const info = await ctx.host.mount(workspaceRoot, {
    workspaceId: optionalString2(p, "workspaceId"),
    tentName: optionalString2(p, "tentName")
  });
  await reconcileTaskSessionsOnMount(ctx, info.workspaceId);
  return info;
}
var SESSION_UNAVAILABLE_WAIT_SUMMARY = "Bound session unavailable (service restart or session ended). Restart the session or interrupt the task; the Task remains active.";
var SESSION_UNAVAILABLE_WAIT_CODE = "session_unavailable";
function isSessionUnavailableParkedWait(task) {
  if (task.state !== "waiting" || task.wait?.reason !== "external") return false;
  if (task.wait.code === SESSION_UNAVAILABLE_WAIT_CODE) return true;
  return task.wait.summary === SESSION_UNAVAILABLE_WAIT_SUMMARY;
}
async function applySessionUnavailablePark(ctx, workspaceId, taskPath, emitReason) {
  const mount = ctx.host.get(workspaceId);
  if (!mount) return null;
  const current = await loadTaskRecord(mount.env.fs, taskPath);
  if (current.state !== "running" && current.state !== "waiting") return null;
  if (isSessionUnavailableParkedWait(current)) return null;
  if (isRejectResumeParkedWait(current)) return null;
  const wait = {
    reason: "external",
    summary: SESSION_UNAVAILABLE_WAIT_SUMMARY,
    code: SESSION_UNAVAILABLE_WAIT_CODE
  };
  let next;
  if (current.state === "running") {
    next = await taskWait(mount.env, taskPath, {
      reason: wait.reason,
      summary: wait.summary,
      code: wait.code
    });
  } else {
    next = await patchTaskRecord(mount.env.fs, taskPath, {
      state: "waiting",
      wait,
      updatedAt: mount.env.clock.now()
    });
  }
  emitTaskState(ctx, workspaceId, next, emitReason);
  return next;
}
async function parkTaskForUnavailableSession(ctx, input) {
  const mount = ctx.host.get(input.workspaceId);
  if (!mount) return false;
  let applied = false;
  let parked = null;
  await ctx.mutations.run(input.workspaceId, async () => {
    ctx.host.markSelfWrite(input.workspaceId, 200, TEMP_DIR);
    const current = await loadTaskRecord(mount.env.fs, input.taskPath);
    if (current.state !== "running" && current.state !== "waiting") return;
    if (input.sessionId && current.executionSessionId && current.executionSessionId !== input.sessionId) {
      return;
    }
    parked = await applySessionUnavailablePark(
      ctx,
      input.workspaceId,
      input.taskPath,
      input.reason
    );
    applied = parked != null;
  });
  if (applied) {
    ctx.events.emit(
      "session.state",
      input.workspaceId,
      {
        sessionId: input.sessionId,
        taskPath: input.taskPath,
        taskState: "waiting",
        waitReason: "external",
        waitCode: SESSION_UNAVAILABLE_WAIT_CODE,
        runtimeEvent: input.reason,
        ...input.detail ? { error: input.detail } : {},
        taskFailed: false,
        recoverable: true
      },
      "service"
    );
  }
  if (applied && input.sessionId) {
    try {
      await ctx.toolApprovals.cancelSession(input.sessionId, "denied");
    } catch {
    }
    try {
      const probe = await ctx.runtime.probe(input.sessionId);
      if (probe.isAlive || SessionRegistry.isNonTerminal(probe.state)) {
        await ctx.runtime.stopSession(input.sessionId, "interrupt");
      }
    } catch {
    }
  }
  return applied;
}
async function reconcileTaskSessionsOnMount(ctx, workspaceId) {
  const mount = ctx.host.require(workspaceId);
  const tasks = await loadTaskRecords(mount.env.fs);
  const reconciled = [];
  for (const task of tasks) {
    if (task.state !== "running" && task.state !== "waiting") continue;
    const sessionId = task.executionSessionId?.trim();
    if (!sessionId) continue;
    const probe = await ctx.runtime.probe(sessionId);
    if (probe.isAlive) continue;
    if (isSessionUnavailableParkedWait(task)) continue;
    await ctx.mutations.run(workspaceId, async () => {
      ctx.host.markSelfWrite(workspaceId, 200, TEMP_DIR);
      const current = await loadTaskRecord(mount.env.fs, task.path);
      if (current.state !== "running" && current.state !== "waiting") return;
      if (current.executionSessionId?.trim() !== sessionId) return;
      const probe2 = await ctx.runtime.probe(sessionId);
      if (probe2.isAlive) return;
      if (isSessionUnavailableParkedWait(current)) return;
      const parked = await applySessionUnavailablePark(
        ctx,
        workspaceId,
        task.path,
        "session.reconcile"
      );
      if (parked) reconciled.push(task.path);
    });
  }
  return { reconciled };
}
async function workspaceUnmount(ctx, p) {
  const workspaceId = requireString(p, "workspaceId");
  await ctx.host.unmount(workspaceId);
  return { ok: true };
}
function workspaceSetForeground(ctx, p) {
  const workspaceId = requireString(p, "workspaceId");
  return ctx.host.setForeground(workspaceId);
}
async function docsList(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  const includeBody = p.includeBody === true;
  return {
    workspaceId,
    nodes: tent.roots.map((root) => projectNode(root, includeBody, true))
  };
}
async function docsGet(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  const node2 = resolveNode2(tent, p);
  return {
    workspaceId,
    node: projectNode(node2, true, false)
  };
}
async function docsReadForEdit(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  const node2 = resolveNode2(tent, p);
  const notePath = nodeNotePath(node2.path);
  const raw = await mount.env.fs.readFile(notePath);
  const { data, body } = parseFrontmatter(raw);
  return {
    workspaceId,
    nodeId: node2.id,
    path: node2.path,
    name: node2.name,
    type: node2.type,
    body,
    raw,
    etag: contentEtag(raw),
    frontmatter: data
  };
}
async function docsWrite(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const baseEtag = optionalString2(p, "baseEtag");
  const rawInput = typeof p.raw === "string" ? p.raw : void 0;
  const body = typeof p.body === "string" ? p.body : void 0;
  const frontmatter = p.frontmatter && typeof p.frontmatter === "object" && !Array.isArray(p.frontmatter) ? p.frontmatter : void 0;
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.write");
    const notePath = nodeNotePath(node2.path);
    const diskRaw = await mount.env.fs.readFile(notePath);
    const currentEtag = contentEtag(diskRaw);
    if (!baseEtag) {
      throw new RpcError(-32008, "docs.write requires baseEtag for existing nodes", {
        code: "etag_required",
        currentEtag,
        path: node2.path,
        nodeId: node2.id
      });
    }
    if (baseEtag !== currentEtag) {
      throw new RpcError(-32009, "etag conflict", {
        code: "etag_conflict",
        currentEtag,
        baseEtag,
        path: node2.path,
        nodeId: node2.id
      });
    }
    if (rawInput !== void 0) {
      const diskParsed = parseFrontmatter(diskRaw);
      const nextParsed = parseFrontmatter(rawInput);
      assertRawDocsWriteReserved(diskParsed.data, nextParsed.data);
      assertRawDocsWriteSemantic(diskParsed.data, nextParsed.data);
      assertRawDocsWriteCollaborationFields(nextParsed.data);
      ctx.host.markSelfWrite(workspaceId);
      await mount.env.fs.writeFile(notePath, rawInput);
      await syncTagRegistryAfterNodeTagsChange(
        mount.env.fs,
        node2.tags,
        tagsFromFrontmatterData(nextParsed.data)
      );
    } else {
      if (frontmatter) {
        assertReservedDocsWriteFields(frontmatter);
        assertSemanticDocsWriteFields(frontmatter);
        assertDocsWriteCollaborationFields(frontmatter);
      }
      ctx.host.markSelfWrite(workspaceId);
      if (frontmatter && Object.keys(frontmatter).length > 0) {
        await patchNode(mount.env, node2.path, frontmatter, tent);
      }
      if (body !== void 0) {
        await patchBody(mount.env, node2.path, body, tent);
      }
      if (body === void 0 && (!frontmatter || Object.keys(frontmatter).length === 0)) {
        throw new RpcError(-32602, "docs.write requires raw, body, and/or frontmatter");
      }
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: node2.id, path: node2.path, reason: "docs.write" },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw)
    };
  });
}
async function docsSetMode(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const modeRaw = requireString(p, "mode");
  if (modeRaw === "read-only") {
    throw new RpcError(
      -32602,
      'docs.setMode: "read-only" is retired in V0.2; use "editable" or "archived"'
    );
  }
  if (modeRaw !== "editable" && modeRaw !== "archived") {
    throw new RpcError(-32602, 'docs.setMode mode must be "editable" or "archived"');
  }
  const mode = modeRaw;
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    ctx.host.markSelfWrite(workspaceId);
    try {
      await setNodeMode(mount.env, node2.id, mode);
    } catch (err) {
      const message2 = err instanceof Error ? err.message : "docs.setMode failed";
      if (/not found/i.test(message2)) throw new RpcError(-32004, message2);
      if (/mode must be|Invalid nodes|archive root|already archived|Claimed ranges|restored to editable/i.test(
        message2
      )) {
        throw new RpcError(-32602, message2);
      }
      throw new RpcError(-32e3, message2);
    }
    const after = await loadTent(mount.env.fs);
    const updated = after.byId.get(node2.id);
    if (!updated) throw new RpcError(-32004, `Node not found after setMode: ${node2.id}`);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: updated.id, path: updated.path, reason: "docs.setMode", mode: updated.mode },
      "self"
    );
    return {
      workspaceId,
      nodeId: updated.id,
      path: updated.path,
      mode: updated.mode,
      archived: updated.archived,
      node: projectNode(updated, false, false)
    };
  });
}
async function docsSearch(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const query = optionalString2(p, "query") ?? optionalString2(p, "q") ?? "";
  const q = query.trim().toLowerCase();
  if (!q) return { workspaceId, hits: [] };
  const tent = await loadTent(mount.env.fs);
  const hits = [];
  for (const node2 of tent.byId.values()) {
    if (node2.archived || node2.invalid) continue;
    const title = typeof node2.fm.title === "string" ? node2.fm.title : node2.name;
    if (node2.name.toLowerCase().includes(q) || title.toLowerCase().includes(q)) {
      hits.push({
        nodeId: node2.id,
        path: node2.path,
        name: node2.name,
        title,
        snippet: title,
        match: "title"
      });
      continue;
    }
    if (node2.path.toLowerCase().includes(q)) {
      hits.push({
        nodeId: node2.id,
        path: node2.path,
        name: node2.name,
        title,
        snippet: node2.path,
        match: "path"
      });
      continue;
    }
    const body = node2.body ?? "";
    const idx = body.toLowerCase().indexOf(q);
    if (idx >= 0) {
      const start = Math.max(0, idx - 40);
      const end = Math.min(body.length, idx + q.length + 40);
      hits.push({
        nodeId: node2.id,
        path: node2.path,
        name: node2.name,
        title,
        snippet: body.slice(start, end).replace(/\s+/g, " ").trim(),
        match: "body"
      });
    }
  }
  return { workspaceId, hits: hits.slice(0, 50) };
}
async function docsBacklinks(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  const node2 = resolveNode2(tent, p);
  const nodes = [...tent.byId.values()].map((b) => ({
    id: b.id,
    path: b.path,
    name: b.name,
    body: b.body,
    notePath: nodeNotePath(b.path)
  }));
  const reverse = buildBacklinkIndex(nodes);
  return {
    workspaceId,
    nodeId: node2.id,
    backlinks: reverse.get(node2.id) ?? []
  };
}
async function registryTags(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const registry = await loadTagRegistry(mount.env.fs);
  return { workspaceId, tags: registry.tags };
}
async function registryTagCreate(ctx, p) {
  requireUserActor(p, "registry.tag.create");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const rawName = requireString(p, "name");
  let tag;
  try {
    tag = normalizeTagName(rawName);
  } catch (err) {
    throw mapTagRegistryError(err, "registry.tag.create");
  }
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    try {
      await addRegistryTag(mount.env.fs, tag);
    } catch (err) {
      throw mapTagRegistryError(err, "registry.tag.create");
    }
    emitRegistryTagsUpdated(ctx, workspaceId, {
      action: "create",
      name: tag
    });
    return { workspaceId, name: tag };
  });
}
async function registryTagDelete(ctx, p) {
  requireUserActor(p, "registry.tag.delete");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const rawName = requireString(p, "name");
  let tag;
  try {
    tag = normalizeTagName(rawName);
  } catch (err) {
    throw mapTagRegistryError(err, "registry.tag.delete");
  }
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    try {
      await removeRegistryTag(mount.env.fs, tag);
    } catch (err) {
      throw mapTagRegistryError(err, "registry.tag.delete");
    }
    emitRegistryTagsUpdated(ctx, workspaceId, {
      action: "delete",
      name: tag
    });
    return { workspaceId, deleted: tag };
  });
}
async function docsSetType(ctx, p) {
  requireUserActor(p, "docs.setType");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  let type;
  try {
    if (!Object.prototype.hasOwnProperty.call(p, "type")) {
      throw new Error("Node type is required.");
    }
    type = p.type === null ? void 0 : normalizeOptionalNodeType(requireString(p, "type"), "Node type");
  } catch (err) {
    throw mapDocsSemanticError(err, "docs.setType");
  }
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.setType");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "docs.setType");
    ctx.host.markSelfWrite(workspaceId);
    try {
      await patchNode(mount.env, node2.path, { type }, tent);
    } catch (err) {
      throw mapDocsSemanticError(err, "docs.setType");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: node2.id, path: node2.path, reason: "docs.setType", type: type ?? null },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw)
    };
  });
}
async function docsTagsSet(ctx, p) {
  requireUserActor(p, "docs.tags.set");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  if (!Array.isArray(p.tags)) {
    throw new RpcError(-32602, "docs.tags.set requires tags: string[]");
  }
  const tagsRaw = p.tags;
  for (const item of tagsRaw) {
    if (typeof item !== "string") {
      throw new RpcError(-32602, "docs.tags.set tags must be an array of strings");
    }
  }
  let tags;
  try {
    tags = [...new Set(tagsRaw.map((t) => normalizeTagName(t)))].sort(
      (a, b) => a.localeCompare(b)
    );
  } catch (err) {
    throw mapDocsSemanticError(err, "docs.tags.set");
  }
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.tags.set");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "docs.tags.set");
    ctx.host.markSelfWrite(workspaceId);
    try {
      await patchNode(mount.env, node2.path, { tags }, tent);
    } catch (err) {
      throw mapDocsSemanticError(err, "docs.tags.set");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: node2.id, path: node2.path, reason: "docs.tags.set" },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw)
    };
  });
}
async function docsTagAdd(ctx, p) {
  requireUserActor(p, "docs.tag.add");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const rawTag = requireString(p, "tag");
  let tag;
  try {
    tag = normalizeTagName(rawTag);
  } catch (err) {
    throw mapDocsSemanticError(err, "docs.tag.add");
  }
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.tag.add");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "docs.tag.add");
    ctx.host.markSelfWrite(workspaceId);
    try {
      await addTag(mount.env.fs, node2.id, tag);
    } catch (err) {
      throw mapDocsSemanticError(err, "docs.tag.add");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: node2.id, path: node2.path, reason: "docs.tag.add", tag },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw)
    };
  });
}
async function docsTagRemove(ctx, p) {
  requireUserActor(p, "docs.tag.remove");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const rawTag = requireString(p, "tag");
  let tag;
  try {
    tag = normalizeTagName(rawTag);
  } catch (err) {
    throw mapDocsSemanticError(err, "docs.tag.remove");
  }
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.tag.remove");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "docs.tag.remove");
    ctx.host.markSelfWrite(workspaceId);
    try {
      await removeTag(mount.env.fs, node2.id, tag);
    } catch (err) {
      throw mapDocsSemanticError(err, "docs.tag.remove");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: node2.id, path: node2.path, reason: "docs.tag.remove", tag },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw)
    };
  });
}
function projectRelationWire(record) {
  const out = {
    id: record.id,
    kind: record.kind,
    direction: record.direction,
    target: "nodeId" in record.target ? { nodeId: record.target.nodeId } : { unresolved: record.target.unresolved }
  };
  if (record.label !== void 0) out.label = record.label;
  return out;
}
function projectIncomingWire(view) {
  return {
    ...projectRelationWire(view),
    sourceNodeId: view.sourceId,
    sourcePath: view.sourcePath
  };
}
async function relationList(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  const node2 = resolveNode2(tent, p);
  try {
    const listed = listRelationsForNode(tent, node2.id);
    return {
      workspaceId,
      nodeId: listed.nodeId,
      path: listed.path,
      outgoing: listed.outgoing.map(projectRelationWire),
      incoming: listed.incoming.map(projectIncomingWire)
    };
  } catch (err) {
    throw mapRelationError(err, "relation.list");
  }
}
async function relationCreate(ctx, p) {
  requireUserActor(p, "relation.create");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const kind = requireString(p, "kind");
  const direction = requireString(p, "direction");
  const label = optionalString2(p, "label");
  if (!isRecord10(p.target)) {
    throw new RpcError(-32602, "relation.create requires target: { nodeId } | { unresolved }");
  }
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "relation.create");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "relation.create");
    ctx.host.markSelfWrite(workspaceId);
    let record;
    try {
      record = await createRelation(
        mount.env.fs,
        node2.id,
        {
          kind,
          direction,
          ...label !== void 0 ? { label } : {},
          target: p.target
        },
        Math.random,
        tent
      );
    } catch (err) {
      throw mapRelationError(err, "relation.create");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      {
        nodeId: node2.id,
        path: node2.path,
        reason: "relation.create",
        relationId: record.id
      },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw),
      relation: projectRelationWire(record)
    };
  });
}
async function relationUpdate(ctx, p) {
  requireUserActor(p, "relation.update");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const relationId = requireString(p, "relationId");
  const baseEtag = optionalString2(p, "baseEtag");
  const patch = {};
  if (Object.prototype.hasOwnProperty.call(p, "kind")) {
    patch.kind = requireString(p, "kind");
  }
  if (Object.prototype.hasOwnProperty.call(p, "direction")) {
    patch.direction = requireString(p, "direction");
  }
  if (Object.prototype.hasOwnProperty.call(p, "label")) {
    const rawLabel = p.label;
    if (rawLabel === null) patch.label = null;
    else if (typeof rawLabel === "string") patch.label = rawLabel;
    else throw new RpcError(-32602, "relation.update label must be string or null");
  }
  if (Object.prototype.hasOwnProperty.call(p, "target")) {
    if (!isRecord10(p.target)) {
      throw new RpcError(-32602, "relation.update target must be { nodeId } | { unresolved }");
    }
    patch.target = p.target;
  }
  if (Object.keys(patch).length === 0) {
    throw new RpcError(
      -32602,
      "relation.update requires at least one of kind, direction, label, target"
    );
  }
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "relation.update");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "relation.update");
    ctx.host.markSelfWrite(workspaceId);
    let record;
    try {
      record = await updateRelation(mount.env.fs, node2.id, relationId, patch, tent);
    } catch (err) {
      throw mapRelationError(err, "relation.update");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      {
        nodeId: node2.id,
        path: node2.path,
        reason: "relation.update",
        relationId: record.id
      },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw),
      relation: projectRelationWire(record)
    };
  });
}
async function relationDelete(ctx, p) {
  requireUserActor(p, "relation.delete");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const relationId = requireString(p, "relationId");
  const baseEtag = optionalString2(p, "baseEtag");
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "relation.delete");
    const notePath = nodeNotePath(node2.path);
    await assertDocsSemanticBaseEtag(mount.env.fs, notePath, node2, baseEtag, "relation.delete");
    ctx.host.markSelfWrite(workspaceId);
    try {
      await deleteRelation(mount.env.fs, node2.id, relationId, tent);
    } catch (err) {
      throw mapRelationError(err, "relation.delete");
    }
    const afterRaw = await mount.env.fs.readFile(notePath);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      {
        nodeId: node2.id,
        path: node2.path,
        reason: "relation.delete",
        relationId
      },
      "self"
    );
    return {
      workspaceId,
      nodeId: node2.id,
      path: node2.path,
      etag: contentEtag(afterRaw),
      deleted: relationId
    };
  });
}
function mapRelationError(err, surface) {
  if (err instanceof RpcError) return err;
  if (err instanceof RelationError) {
    if (err.code === "NOT_FOUND") return new RpcError(-32004, err.message);
    if (err.code === "ARCHIVED" || err.code === "INVALID") {
      return new RpcError(-32010, `${surface} rejected: ${err.message}`, { code: err.code });
    }
    if (err.code === "CORRUPT") {
      return new RpcError(-32602, err.message, {
        code: "relations_corrupt",
        reason: err.code
      });
    }
    if (err.code === "TARGET" || err.code === "INVALID_INPUT") {
      return new RpcError(-32602, err.message, { code: err.code });
    }
    return new RpcError(-32e3, err.message, { code: err.code });
  }
  const message2 = err instanceof Error ? err.message : `${surface} failed`;
  if (/not found|Node not found|Node not found|Relation not found/i.test(message2)) {
    return new RpcError(-32004, message2);
  }
  if (/archived|invalid/i.test(message2)) {
    return new RpcError(-32010, message2);
  }
  if (/corrupt|non-canonical|unrecognized field|duplicate id/i.test(message2)) {
    return new RpcError(-32602, message2, { code: "relations_corrupt" });
  }
  return new RpcError(-32e3, message2);
}
function isRecord10(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}
function emitRegistryTagsUpdated(ctx, workspaceId, payload) {
  ctx.events.emit(
    "registry.tags.updated",
    workspaceId,
    { action: payload.action, name: payload.name },
    "self"
  );
}
async function assertDocsSemanticBaseEtag(fs21, notePath, node2, baseEtag, surface) {
  const diskRaw = await fs21.readFile(notePath);
  const currentEtag = contentEtag(diskRaw);
  if (!baseEtag) {
    throw new RpcError(-32008, `${surface} requires baseEtag`, {
      code: "etag_required",
      currentEtag,
      path: node2.path,
      nodeId: node2.id
    });
  }
  if (baseEtag !== currentEtag) {
    throw new RpcError(-32009, "etag conflict", {
      code: "etag_conflict",
      currentEtag,
      baseEtag,
      path: node2.path,
      nodeId: node2.id
    });
  }
}
function mapTagRegistryError(err, surface) {
  if (err instanceof RpcError) return err;
  const message2 = err instanceof Error ? err.message : `${surface} failed`;
  if (/cannot be empty|path separators|newlines/i.test(message2)) {
    return new RpcError(-32602, message2);
  }
  return new RpcError(-32e3, message2);
}
function mapDocsSemanticError(err, surface) {
  if (err instanceof RpcError) return err;
  const message2 = err instanceof Error ? err.message : `${surface} failed`;
  if (/not found|Node not found/i.test(message2)) {
    return new RpcError(-32004, message2);
  }
  if (/Unknown type|Primary type|Invalid or archived|cannot be tagged|cannot be empty|path separators|newlines|Reserved or retired|Archived nodes|Invalid subtrees/i.test(
    message2
  )) {
    return new RpcError(-32602, message2);
  }
  return new RpcError(-32e3, message2);
}
async function registryRoles(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const registry = await loadRolesRegistry(mount.env.fs);
  const roles = registry.roles.map((role) => projectRoleRegistryEntry(role)).sort((a, b) => a.name.localeCompare(b.name));
  return { workspaceId, roles };
}
function projectRoleRegistryEntry(role) {
  const proj = {
    roleId: role.id ?? "",
    name: role.name,
    displayName: role.displayName || role.name,
    description: role.description,
    color: role.color,
    prompt: role.prompt
  };
  return proj;
}
async function projectRoleRegistryEntryLive(_ctx, role) {
  return projectRoleRegistryEntry(role);
}
async function registryRoleCreate(ctx, p) {
  requireUserActor(p, "registry.role.create");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  if ("id" in p || "roleId" in p) {
    throw new RpcError(
      -32602,
      "registry.role.create does not accept client-supplied id/roleId; server assigns rl- handles"
    );
  }
  const definition2 = parseRoleDefinitionParams(p, { requireName: true });
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    try {
      await createRole(mount.env.fs, definition2);
    } catch (err) {
      throw mapRoleRegistryError(err, "registry.role.create");
    }
    const registry = await loadRolesRegistry(mount.env.fs);
    const role = resolveRole(registry.roles, definition2.name);
    if (!role) {
      throw new RpcError(-32e3, `Role create succeeded but role not found: ${definition2.name}`);
    }
    emitRegistryRolesUpdated(ctx, workspaceId, {
      action: "create",
      name: role.name,
      roleId: role.id || "",
      displayName: role.displayName || role.name
    });
    return {
      workspaceId,
      role: await projectRoleRegistryEntryLive(ctx, role)
    };
  });
}
async function registryRoleUpdate(ctx, p) {
  requireUserActor(p, "registry.role.update");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const ref = typeof p.roleId === "string" && p.roleId.trim() ? p.roleId.trim() : requireString(p, "name");
  if (p.rename !== void 0 || typeof p.newName === "string" && p.newName.trim()) {
    throw new RpcError(
      -32602,
      "registry.role.update cannot rename operational name in this batch; pass displayName to change the label"
    );
  }
  if (typeof p.patch === "object" && p.patch !== null && !Array.isArray(p.patch)) {
    throw new RpcError(
      -32602,
      "registry.role.update does not accept nested patch; pass fields at the top level with name or roleId"
    );
  }
  if ("id" in p) {
    throw new RpcError(-32602, "registry.role.update cannot change id; role id is immutable");
  }
  const patch = parseRoleDefinitionParams(p, { requireName: false, forUpdate: true });
  const { name: _ignoredName, id: _ignoredId, displayName: _ignoredDn, ...fields } = patch;
  const updatePatch = { ...fields };
  for (const key2 of ["prompt", "description", "color"]) {
    if (key2 in p && (p[key2] === null || typeof p[key2] === "string" && !p[key2].trim())) {
      updatePatch[key2] = void 0;
    }
  }
  if ("displayName" in p) {
    if (p.displayName === null || typeof p.displayName === "string" && !p.displayName.trim()) {
      updatePatch.displayName = void 0;
    } else if (typeof p.displayName === "string") {
      updatePatch.displayName = p.displayName;
    }
  }
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    try {
      await updateRole(mount.env.fs, ref, updatePatch);
    } catch (err) {
      throw mapRoleRegistryError(err, "registry.role.update");
    }
    const registry = await loadRolesRegistry(mount.env.fs);
    const role = resolveRole(registry.roles, ref);
    if (!role) {
      throw new RpcError(-32004, `Role does not exist: ${ref}`);
    }
    emitRegistryRolesUpdated(ctx, workspaceId, {
      action: "update",
      name: role.name,
      roleId: role.id || "",
      displayName: role.displayName || role.name
    });
    return {
      workspaceId,
      role: await projectRoleRegistryEntryLive(ctx, role)
    };
  });
}
async function registryRoleDelete(ctx, p) {
  requireUserActor(p, "registry.role.delete");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const ref = typeof p.roleId === "string" && p.roleId.trim() ? p.roleId.trim() : requireString(p, "name");
  const confirmation = requireString(p, "confirmation");
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const registry = await loadRolesRegistry(mount.env.fs);
    const existing = resolveRole(registry.roles, ref);
    if (!existing) {
      throw new RpcError(-32004, `Role does not exist: ${ref}`);
    }
    const roleId = existing.id || "";
    if (confirmation !== existing.name && confirmation !== roleId) {
      throw new RpcError(
        -32602,
        `Confirmation mismatch; enter the role name ${existing.name} or id ${roleId}.`,
        { name: existing.name, roleId, confirmation }
      );
    }
    const tasks = await loadTaskRecords(mount.env.fs);
    const activeTask = tasks.find(
      (t) => isActiveTaskState(t.state) && roleId !== "" && t.assigneeRoleId === roleId
    );
    if (activeTask) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Cannot delete role "${existing.name}": active task ${activeTask.path} (state=${activeTask.state})`,
        {
          role: existing.name,
          roleId,
          taskPath: activeTask.path,
          taskState: activeTask.state
        }
      );
    }
    const activeSession = await findActiveExternalSessionForRole(ctx, workspaceId, roleId);
    if (activeSession) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Cannot delete role "${existing.name}": active managed session ${activeSession.id} (state=${activeSession.state})`,
        {
          role: existing.name,
          roleId,
          sessionId: activeSession.id,
          sessionState: activeSession.state
        }
      );
    }
    try {
      await deleteRole(mount.env.fs, roleId || existing.name, confirmation);
    } catch (err) {
      throw mapRoleRegistryError(err, "registry.role.delete");
    }
    emitRegistryRolesUpdated(ctx, workspaceId, {
      action: "delete",
      name: existing.name,
      roleId,
      displayName: existing.displayName || existing.name
    });
    return {
      workspaceId,
      deleted: existing.name,
      roleId,
      displayName: existing.displayName || existing.name
    };
  });
}
function emitRegistryRolesUpdated(ctx, workspaceId, payload) {
  ctx.events.emit(
    "registry.roles.updated",
    workspaceId,
    {
      action: payload.action,
      name: payload.name,
      roleId: payload.roleId,
      displayName: payload.displayName
    },
    "self"
  );
}
function parseRoleDefinitionParams(p, opts) {
  const surface = opts.forUpdate ? "registry.role.update" : "registry.role.create";
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set([
      "workspaceId",
      "actor",
      "name",
      ...opts.forUpdate ? ["roleId"] : [],
      "displayName",
      "prompt",
      "description",
      "color"
    ]),
    surface
  );
  const raw = {};
  if (opts.requireName || typeof p.name === "string") {
    raw.name = requireString(p, "name");
  } else if (!opts.forUpdate) {
    throw new RpcError(-32602, "Missing string param: name");
  } else {
    raw.name = "";
  }
  if ("displayName" in p) {
    if (p.displayName !== void 0 && p.displayName !== null && typeof p.displayName !== "string") {
      throw new RpcError(-32602, "Invalid string param: displayName");
    }
    if (typeof p.displayName === "string") raw.displayName = p.displayName;
  }
  if ("prompt" in p) {
    if (p.prompt !== void 0 && p.prompt !== null && typeof p.prompt !== "string") {
      throw new RpcError(-32602, "Invalid string param: prompt");
    }
    if (typeof p.prompt === "string") raw.prompt = p.prompt;
  }
  if ("description" in p) {
    if (p.description !== void 0 && p.description !== null && typeof p.description !== "string") {
      throw new RpcError(-32602, "Invalid string param: description");
    }
    if (typeof p.description === "string") raw.description = p.description;
  }
  if ("color" in p) {
    if (p.color !== void 0 && p.color !== null && typeof p.color !== "string") {
      throw new RpcError(-32602, "Invalid string param: color");
    }
    if (typeof p.color === "string") raw.color = p.color;
  }
  try {
    const role = normalizeRoleDefinition(raw);
    if (opts.requireName && !role.name) {
      throw new RpcError(-32602, "Role name cannot be empty.");
    }
    return role;
  } catch (err) {
    if (err instanceof RpcError) throw err;
    const message2 = err instanceof Error ? err.message : "Invalid role definition";
    throw new RpcError(-32602, message2);
  }
}
function mapRoleRegistryError(err, surface) {
  if (err instanceof RpcError) return err;
  const message2 = err instanceof Error ? err.message : `${surface} failed`;
  if (/already exists|does not exist|Confirmation mismatch|cannot be empty|immutable|cannot be renamed/i.test(
    message2
  )) {
    if (/does not exist/i.test(message2)) {
      return new RpcError(-32004, message2);
    }
    return new RpcError(-32602, message2);
  }
  return new RpcError(-32e3, message2);
}
async function connectionList(ctx, p) {
  void p;
  const catalog = ctx.connectionCatalog.list();
  const existsMap = await launchSecretExistsLookup(ctx, catalog);
  return { connections: projectAgentConnections(catalog, { launchSecretExistsById: existsMap }) };
}
async function connectionGet(ctx, p) {
  const connectionId = requireString(p, "connectionId");
  const connection = ctx.connectionCatalog.get(connectionId);
  if (!connection) {
    throw new RpcError(-32004, `Agent Connection not found: ${connectionId}`);
  }
  return {
    connection: projectAgentConnection(
      connection,
      await connectionLaunchSecretExistsOpts(ctx, connection)
    )
  };
}
async function connectionCreate(ctx, p) {
  const created = await ctx.connectionCatalog.create(p);
  const connection = projectAgentConnection(
    created,
    await connectionLaunchSecretExistsOpts(ctx, created)
  );
  ctx.events.emit(
    "connection.changed",
    "",
    { action: "create", connectionId: created.connectionId, connection },
    "self"
  );
  return {
    connection
  };
}
async function connectionUpdate(ctx, p) {
  const connectionId = requireString(p, "connectionId");
  const { connectionId: _connectionId, ...patch } = p;
  const updated = await ctx.connectionCatalog.update(connectionId, patch);
  const connection = projectAgentConnection(
    updated,
    await connectionLaunchSecretExistsOpts(ctx, updated)
  );
  ctx.events.emit(
    "connection.changed",
    "",
    { action: "update", connectionId: updated.connectionId, connection },
    "self"
  );
  return {
    connection
  };
}
async function connectionDelete(ctx, p) {
  const connectionId = requireString(p, "connectionId");
  const result = await ctx.connectionCatalog.delete(connectionId);
  ctx.events.emit(
    "connection.changed",
    "",
    { action: "delete", connectionId: result.deleted },
    "self"
  );
  return result;
}
async function launchSecretExistsLookup(ctx, routes) {
  const map = /* @__PURE__ */ new Map();
  for (const route of routes) {
    const ref = typeof route.launchSecretRef === "string" ? route.launchSecretRef.trim() : "";
    if (ref && !map.has(ref)) {
      map.set(ref, ctx.launchSecrets.has(ref));
    }
  }
  return map;
}
async function connectionLaunchSecretExistsOpts(ctx, connection) {
  const ref = connection.launchSecretRef?.trim() || void 0;
  if (!ref) return void 0;
  return { launchSecretExists: ctx.launchSecrets.has(ref) };
}
function providerCatalogRpc() {
  return projectProviderCatalog();
}
function requireMachineSettingsCaller(p, surface, callContext) {
  if (callContext.callerSessionId || callContext.callerExternalKey) {
    throw new RpcError(
      -32001,
      `${surface} is available only to the local machine Settings client`,
      {
        code: "MACHINE_SETTINGS_CALLER_REQUIRED",
        ...callContext.callerSessionId ? { callerSessionId: callContext.callerSessionId } : {},
        ...callContext.callerExternalKey ? { callerExternalKey: callContext.callerExternalKey } : {}
      }
    );
  }
  requireUserActor(p, surface);
}
async function settingsLaunchSecretList(ctx, p, callContext) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["actor"]), "settings.launchSecret.list");
  requireMachineSettingsCaller(p, "settings.launchSecret.list", callContext);
  const launchSecrets = await ctx.launchSecrets.list();
  return { launchSecrets };
}
async function settingsLaunchSecretSet(ctx, p, callContext) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["id", "secret", "label", "actor"]),
    "settings.launchSecret.set"
  );
  requireMachineSettingsCaller(p, "settings.launchSecret.set", callContext);
  const id = requireString(p, "id");
  if (!("secret" in p) || typeof p.secret !== "string" || p.secret.length === 0) {
    throw new RpcError(-32602, "Missing or invalid string param: secret");
  }
  const secret = p.secret;
  let metadata;
  if ("label" in p && p.label !== void 0 && p.label !== null) {
    if (typeof p.label !== "string") {
      throw new RpcError(-32602, "Invalid string param: label");
    }
    metadata = { label: p.label };
  }
  try {
    const launchSecret = await ctx.launchSecrets.set(id, secret, metadata);
    ctx.events.emit(
      "settings.launchSecret.changed",
      "",
      {
        action: "set",
        id: launchSecret.id,
        updatedAt: launchSecret.updatedAt,
        ...launchSecret.metadata ? { metadata: launchSecret.metadata } : {}
      },
      "self"
    );
    return { launchSecret };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : "settings.launchSecret.set failed";
    if (secret && message2.includes(secret)) {
      throw new RpcError(-32602, "settings.launchSecret.set failed");
    }
    if (/Invalid launch secret id|Missing or invalid launch secret|launch secret|metadata|must match/i.test(
      message2
    )) {
      throw new RpcError(-32602, message2);
    }
    throw new RpcError(-32e3, message2);
  }
}
async function settingsLaunchSecretDelete(ctx, p, callContext) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["id", "actor"]), "settings.launchSecret.delete");
  requireMachineSettingsCaller(p, "settings.launchSecret.delete", callContext);
  const id = requireString(p, "id");
  try {
    const result = await ctx.launchSecrets.delete(id);
    ctx.events.emit(
      "settings.launchSecret.changed",
      "",
      { action: "delete", id: result.deleted },
      "self"
    );
    return result;
  } catch (err) {
    const message2 = err instanceof Error ? err.message : "settings.launchSecret.delete failed";
    if (/not found/i.test(message2)) {
      throw new RpcError(-32004, message2);
    }
    if (/Invalid launch secret id|Missing or invalid launch secret/i.test(message2)) {
      throw new RpcError(-32602, message2);
    }
    throw new RpcError(-32e3, message2);
  }
}
async function skillList(ctx) {
  try {
    return await listSkills({ packageRoot: ctx.packageRoot, home: ctx.home });
  } catch (err) {
    const message2 = err instanceof Error ? err.message : "skill.list failed";
    throw new RpcError(-32e3, message2);
  }
}
async function skillInstall(ctx, p) {
  for (const banned of ["source", "destination", "dest", "dir", "targetDir", "targetDirs", "path"]) {
    if (banned in p) {
      throw new RpcError(
        -32602,
        `skill.install does not accept ${banned}; only skills[], targets[], force`
      );
    }
  }
  if ("workspaceId" in p && p.workspaceId !== void 0 && p.workspaceId !== null) {
    throw new RpcError(-32602, "skill.install is machine-local and does not accept workspaceId");
  }
  let skills;
  if ("skills" in p && p.skills !== void 0 && p.skills !== null) {
    if (!Array.isArray(p.skills) || !p.skills.every((s) => typeof s === "string")) {
      throw new RpcError(-32602, "Invalid skills: must be an array of strings when set");
    }
    skills = p.skills;
  }
  let targets;
  if ("targets" in p && p.targets !== void 0 && p.targets !== null) {
    if (!Array.isArray(p.targets) || !p.targets.every((t) => typeof t === "string")) {
      throw new RpcError(-32602, "Invalid targets: must be an array of strings when set");
    }
    try {
      targets = p.targets.map((t) => parseSkillTargetId(t));
    } catch (err) {
      const message2 = err instanceof Error ? err.message : "Invalid targets";
      throw new RpcError(-32602, message2);
    }
  }
  let force = false;
  if ("force" in p && p.force !== void 0 && p.force !== null) {
    if (typeof p.force !== "boolean") {
      throw new RpcError(-32602, "Invalid force: must be a boolean when set");
    }
    force = p.force;
  }
  try {
    const results = await installSkills({
      packageRoot: ctx.packageRoot,
      home: ctx.home,
      skills,
      targets,
      force
    });
    ctx.events.emit(
      "skill.changed",
      "",
      {
        action: "install",
        installed: results.filter((r) => r.status === "installed").length,
        skipped: results.filter((r) => r.status === "skipped").length
      },
      "self"
    );
    return { results };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : "skill.install failed";
    if (/Invalid skill name|Unknown skill target|Unknown bundled skill|escapes the destination/i.test(
      message2
    )) {
      throw new RpcError(-32602, message2);
    }
    throw new RpcError(-32e3, message2);
  }
}
async function docsCreateNote(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const name = requireString(p, "name");
  const type = optionalString2(p, "type") ?? "prompt";
  const parentPath = optionalString2(p, "parentPath") ?? "";
  const body = typeof p.body === "string" ? p.body : void 0;
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const nodeId = await createNode(mount.env, { parentPath, name, type });
    const notePath = parentPath ? `${parentPath}/${name}` : name;
    if (body !== void 0) {
      await patchBody(mount.env, notePath, body.endsWith("\n") ? body : body + "\n");
    }
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId, path: notePath, reason: "docs.createNote" },
      "self"
    );
    return { workspaceId, nodeId, path: notePath, type };
  });
}
async function docsImportAttachment(ctx, p) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "nodeId", "fileName", "bytesBase64"]),
    "docs.importAttachment"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const fileName = requireString(p, "fileName");
  const rawBase64 = typeof p.bytesBase64 === "string" ? p.bytesBase64 : void 0;
  if (rawBase64 === void 0) {
    throw new RpcError(
      -32602,
      "docs.importAttachment requires bytesBase64 (base64-encoded file bytes)"
    );
  }
  let bytes;
  try {
    bytes = rawBase64 === "" ? new Uint8Array() : decodeBase64Strict(rawBase64);
  } catch (err) {
    const message2 = err instanceof Error ? err.message : "Invalid base64";
    throw new RpcError(-32602, message2);
  }
  if (bytes.byteLength > MAX_ATTACHMENT_BYTES) {
    throw new RpcError(
      -32602,
      `Attachment exceeds max size of ${MAX_ATTACHMENT_BYTES} bytes (${bytes.byteLength} bytes)`,
      { maxBytes: MAX_ATTACHMENT_BYTES, byteLength: bytes.byteLength }
    );
  }
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    assertDocsModeMutable(node2, "docs.importAttachment");
    ctx.host.markSelfWrite(workspaceId);
    try {
      const result = await storeAttachmentBytes(
        mount.env.fs,
        node2.id,
        fileName,
        bytes,
        nodeNotePath(node2.path)
      );
      return {
        workspaceId,
        nodeId: node2.id,
        relativePath: result.relativePath,
        markdown: result.markdown
      };
    } catch (err) {
      const message2 = err instanceof Error ? err.message : "importAttachment failed";
      if (/exceeds max size|Invalid base64|path rejected|file name/i.test(message2)) {
        throw new RpcError(-32602, message2);
      }
      throw new RpcError(-32e3, message2);
    }
  });
}
async function docsFork(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = requireString(p, "nodeId");
  return ctx.mutations.run(workspaceId, async () => {
    if (!isNodeId(nodeId)) throw new RpcError(-32602, `Invalid Node id: ${nodeId}`);
    const tent = await loadTent(mount.env.fs);
    requireCanonicalNode(tent, nodeId);
    ctx.host.markSelfWrite(workspaceId);
    const forkRootId = await forkNode(mount.env, nodeId);
    ctx.events.emit(
      "node.changed",
      workspaceId,
      { nodeId: forkRootId, reason: "docs.fork", forkOf: nodeId },
      "self"
    );
    return { workspaceId, nodeId: forkRootId, forkOf: nodeId };
  });
}
async function docsRename(ctx, p) {
  requireUserActor(p, "docs.rename");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const newName = requireString(p, "newName");
  if ("newId" in p) {
    throw new RpcError(-32602, "docs.rename cannot change node id; cx- is immutable");
  }
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = resolveNode2(tent, p);
    ctx.host.markSelfWrite(workspaceId);
    try {
      const result = await renameNode(mount.env, node2.id, newName);
      ctx.events.emit(
        "node.changed",
        workspaceId,
        {
          nodeId: result.id,
          path: result.path,
          oldPath: result.oldPath,
          name: result.name,
          reason: "docs.rename",
          pathMap: result.pathMap
        },
        "self"
      );
      return {
        workspaceId,
        nodeId: result.id,
        path: result.path,
        oldPath: result.oldPath,
        name: result.name,
        pathMap: result.pathMap,
        rewrittenNotes: result.rewrittenNotes
      };
    } catch (err) {
      throw mapDocsRenameError(err);
    }
  });
}
function mapDocsRenameError(err) {
  if (err instanceof RpcError) return err;
  const message2 = err instanceof Error ? err.message : "docs.rename failed";
  if (/not found/i.test(message2)) {
    return new RpcError(-32004, message2);
  }
  if (/already exists|cannot be empty|path separators|control characters|newlines|longer than|Invalid or archived|Claimed ranges|Cannot rename|System directories|system pipelines|sibling node|Identity note missing|id drift|immutable/i.test(
    message2
  )) {
    return new RpcError(-32602, message2);
  }
  return new RpcError(-32e3, message2);
}
async function docsMove(ctx, p) {
  requireUserActor(p, "docs.move");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = requireString(p, "nodeId");
  const expectedPath = requireString(p, "expectedPath");
  if ("newId" in p) {
    throw new RpcError(-32602, "docs.move cannot change node id; cx- is immutable");
  }
  if ("newParentId" in p && p.newParentId !== null && p.newParentId !== void 0 && typeof p.newParentId !== "string") {
    throw new RpcError(-32602, "docs.move newParentId must be a string or null");
  }
  const newParentId = p.newParentId === null || p.newParentId === void 0 || p.newParentId === "" ? null : String(p.newParentId);
  const position2 = parseMovePosition(p.position);
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = tent.byId.get(nodeId);
    if (!node2) {
      throw new RpcError(-32004, `Node not found: ${nodeId}`);
    }
    const normalizedExpected = expectedPath.replace(/\\/g, "/").replace(/^\.\//, "");
    const currentPath = node2.path;
    if (currentPath !== normalizedExpected) {
      throw new RpcError(-32009, "path stale", {
        code: "path_stale",
        currentPath,
        expectedPath: normalizedExpected,
        nodeId: node2.id
      });
    }
    if (newParentId !== null) {
      const parent = tent.byId.get(newParentId);
      if (!parent) {
        throw new RpcError(-32004, `Target parent not found: ${newParentId}`);
      }
    }
    ctx.host.markSelfWrite(workspaceId);
    try {
      const result = await moveNode(mount.env, node2.id, newParentId, position2);
      ctx.events.emit(
        "node.changed",
        workspaceId,
        {
          nodeId: result.id,
          path: result.path,
          oldPath: result.oldPath,
          reason: "docs.move",
          pathMap: result.pathMap
        },
        "self"
      );
      return {
        workspaceId,
        nodeId: result.id,
        path: result.path,
        oldPath: result.oldPath,
        pathMap: result.pathMap,
        rewrittenNotes: result.rewrittenNotes
      };
    } catch (err) {
      throw mapDocsMoveError(err);
    }
  });
}
function parseMovePosition(raw) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    throw new RpcError(-32602, "docs.move position must be an object");
  }
  const pos = raw;
  const mode = pos.mode;
  if (mode === "inside") {
    return { mode: "inside" };
  }
  if (mode === "before" || mode === "after") {
    const siblingId = pos.siblingId;
    if (typeof siblingId !== "string" || !siblingId.trim()) {
      throw new RpcError(-32602, `docs.move position.${mode} requires siblingId`);
    }
    return { mode, siblingId: siblingId.trim() };
  }
  throw new RpcError(-32602, "docs.move position.mode must be inside, before, or after");
}
function mapDocsMoveError(err) {
  if (err instanceof RpcError) return err;
  const message2 = err instanceof Error ? err.message : "docs.move failed";
  if (/not found/i.test(message2)) {
    return new RpcError(-32004, message2);
  }
  if (/already exists|Invalid or archived|Cannot move|System directories|system pipelines|sibling node|id drift|immutable|own subtree|relative to itself|destination parent|Node id is required/i.test(
    message2
  )) {
    return new RpcError(-32602, message2);
  }
  return new RpcError(-32e3, message2);
}
async function taskDispatch(ctx, p, callContext) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set([
      "workspaceId",
      "nodeIds",
      "assigneeRoleId",
      "connectionId",
      "prompt",
      "requester",
      "acceptMode"
    ]),
    "task.dispatch"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const dispatchSelection = resolveTaskNodeSelection(p);
  const requestedRoleId = optionalString2(p, "assigneeRoleId");
  const connectionId = optionalString2(p, "connectionId");
  if (Boolean(requestedRoleId) === Boolean(connectionId)) {
    throw new RpcError(-32602, "task.dispatch requires exactly one of assigneeRoleId or connectionId");
  }
  const prompt = requireString(p, "prompt");
  const explicitRequester = parseOptionalTaskActor(p.requester, "requester");
  const explicitAcceptMode = parseAcceptMode(optionalString2(p, "acceptMode"));
  const requester = resolveDispatchRequesterFromRpc(explicitRequester);
  if (requester.kind === "role") {
    await requireRoleClaimCallerSession(
      ctx,
      workspaceId,
      requester.id,
      {
        sessionId: callContext.callerSessionId,
        externalKey: callContext.callerExternalKey
      },
      "task.dispatch"
    );
  }
  const isSubDispatch = requester.kind === "role" && requester.id !== requestedRoleId;
  const roleRegistry = requestedRoleId || isSubDispatch ? await loadRolesRegistry(mount.env.fs) : void 0;
  const roleDefinition = requestedRoleId ? roleRegistry?.roles.find((role) => role.id === requestedRoleId) : void 0;
  if (requestedRoleId && !roleDefinition) {
    throw new RpcError(-32004, `Role not found in registry: ${requestedRoleId}`);
  }
  const parentRoleDefinition = isSubDispatch ? roleRegistry?.roles.find((role) => role.id === requester.id) : void 0;
  if (isSubDispatch && !parentRoleDefinition) {
    throw new RpcError(
      -32004,
      `Parent Role not found in registry: ${requester.id}`
    );
  }
  if (connectionId && !ctx.connectionCatalog.get(connectionId)) {
    throw new RpcError(-32004, `Agent Connection not found: ${connectionId}`);
  }
  const callerKind = requester.kind;
  const preallocatedTaskId = connectionId ? makeTaskId() : void 0;
  const reservedSessionId = connectionId ? makeSessionId() : void 0;
  const result = await ctx.mutations.run(workspaceId, async () => {
    if (isSubDispatch) {
      await assertDownstreamDispatchPreconditions(mount.env.fs, {
        workspaceRoot: mount.workspaceRoot,
        requester,
        targetRoleId: requestedRoleId
      });
    }
    let workspaceLane2;
    if (isSubDispatch) {
      const dispatcherLane = await ensureRoleWorkspaceIfGit(
        mount.workspaceRoot,
        parentRoleDefinition.name
      );
      if (requestedRoleId) {
        await ensureRoleWorkspaceIfGit(mount.workspaceRoot, roleDefinition.name);
        workspaceLane2 = void 0;
      } else {
        workspaceLane2 = await ensureTaskWorkspaceIfGit(
          mount.workspaceRoot,
          preallocatedTaskId,
          dispatcherLane ? { targetBranch: dispatcherLane.branch } : {}
        );
      }
    } else if (requestedRoleId) {
      await ensureRoleWorkspaceIfGit(mount.workspaceRoot, roleDefinition.name);
      workspaceLane2 = void 0;
    } else {
      workspaceLane2 = await ensureTaskWorkspaceIfGit(
        mount.workspaceRoot,
        preallocatedTaskId
      );
    }
    ctx.host.markSelfWrite(workspaceId);
    let acceptMode = explicitAcceptMode;
    if (acceptMode === void 0) {
      const settings = await loadWorkspaceSettings(mount.env.fs);
      acceptMode = settings.defaultAcceptMode;
    }
    if (acceptMode !== "review-required" && !allowsNonReviewAcceptMode({
      requester
    })) {
      if (explicitAcceptMode !== void 0) {
        throw new RpcError(
          -32602,
          `acceptMode=${acceptMode} is only legal for a user-facing Task; Task Agent \u2192 parent must use review-required (parent=${requester.kind}:${requester.id})`,
          {
            acceptMode,
            requester,
            roleId: requestedRoleId
          }
        );
      }
      acceptMode = "review-required";
    }
    if (connectionId) {
      await ctx.runtime.reserveSession({
        sessionId: reservedSessionId,
        connectionId,
        currentTaskId: preallocatedTaskId,
        workspace: workspaceId,
        workspaceLane: workspaceLane2,
        runtimeWorkspace: { cwd: workspaceLane2?.worktree || mount.workspaceRoot }
      });
    }
    let dispatched2;
    try {
      dispatched2 = await dispatch(mount.env, {
        prompt,
        requester,
        acceptMode,
        workspace: workspaceLane2,
        ...requestedRoleId ? { assigneeRoleId: requestedRoleId } : {},
        ...reservedSessionId ? { executionSessionId: reservedSessionId } : {},
        nodeIds: dispatchSelection.nodeIds,
        ...preallocatedTaskId ? { taskId: preallocatedTaskId } : {}
      });
    } catch (error) {
      if (reservedSessionId) {
        try {
          await ctx.runtime.registry.remove(reservedSessionId);
        } catch (cleanupError) {
          throw new RpcError(
            RPC_LIFECYCLE,
            "Task creation failed and the exact reserved Session could not be removed",
            {
              code: "DISPATCH_RESERVATION_CLEANUP_FAILED",
              sessionId: reservedSessionId,
              cause: error instanceof Error ? error.message : String(error),
              cleanupError: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
            }
          );
        }
      }
      throw error;
    }
    let dispatchedState = "queued";
    if (connectionId) {
      try {
        if (beforeTaskClaimCoreForTests) {
          const pre = await loadTaskRecord(mount.env.fs, dispatched2.taskPath);
          await beforeTaskClaimCoreForTests({
            workspaceId,
            taskPath: dispatched2.taskPath,
            task: pre
          });
        }
        const claimed = await taskClaim(mount.env, dispatched2.taskPath);
        dispatchedState = claimed.state;
      } catch (error) {
        const detail = error instanceof Error ? error.message : String(error);
        const recoveryErrors = [];
        try {
          const current = await loadTaskRecord(mount.env.fs, dispatched2.taskPath);
          let terminal;
          if (current.state === "queued") {
            ctx.host.markSelfWrite(workspaceId);
            terminal = await patchTaskRecord(mount.env.fs, dispatched2.taskPath, {
              state: "interrupted",
              wait: null,
              updatedAt: mount.env.clock.now()
            });
          } else if (current.state === "running" || current.state === "waiting") {
            terminal = await taskFail(mount.env, dispatched2.taskPath, {
              summary: `Connection dispatch could not claim the exact Task: ${detail}`
            });
          } else {
            terminal = current;
          }
          emitTaskState(ctx, workspaceId, terminal, "task.dispatch.claim-failed");
        } catch (recoveryError) {
          recoveryErrors.push(
            `Task failure: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`
          );
        }
        try {
          await ctx.runtime.registry.update(reservedSessionId, {
            state: "failed",
            lastError: `Connection dispatch could not claim the exact Task: ${detail}`
          });
        } catch (recoveryError) {
          recoveryErrors.push(
            `Session failure: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`
          );
        }
        if (recoveryErrors.length > 0) {
          throw new RpcError(
            RPC_LIFECYCLE,
            `Connection dispatch claim failed and durable recovery was incomplete: ${recoveryErrors.join("; ")}`,
            {
              code: "DISPATCH_CLAIM_RECOVERY_FAILED",
              taskPath: dispatched2.taskPath,
              sessionId: reservedSessionId,
              cause: detail,
              recoveryErrors
            }
          );
        }
        throw error;
      }
    }
    ctx.events.emit(
      "task.state",
      workspaceId,
      {
        path: dispatched2.taskPath,
        state: dispatchedState,
        assigneeRoleId: dispatched2.assigneeRoleId,
        executionSessionId: dispatched2.executionSessionId,
        nodeIds: [...dispatchSelection.nodeIds],
        reason: "task.dispatch"
      },
      "self"
    );
    return { dispatched: dispatched2, workspaceLane: workspaceLane2 };
  });
  const workspaceLane = result.workspaceLane;
  const dispatched = result.dispatched;
  let session = void 0;
  if (connectionId) {
    try {
      session = await taskStartSessionRpc(ctx, {
        workspaceId,
        taskPath: dispatched.taskPath,
        callerKind
      });
    } catch (error) {
      const detail = error instanceof Error ? error.message : String(error);
      await ctx.runtime.registry.update(reservedSessionId, {
        state: "failed",
        lastError: `Connection dispatch could not start the exact Task Session: ${detail}`
      }).catch(() => void 0);
      const current = await loadTaskRecord(mount.env.fs, dispatched.taskPath).catch(() => null);
      if (current && current.state === "running" && current.executionSessionId === reservedSessionId) {
        await parkTaskForUnavailableSession(ctx, {
          workspaceId,
          taskPath: dispatched.taskPath,
          sessionId: reservedSessionId,
          reason: "task.dispatch.connection-start-failed",
          detail
        });
      }
      throw error;
    }
  }
  const taskAfter = await loadTaskRecord(mount.env.fs, dispatched.taskPath);
  return {
    workspaceId,
    taskPath: dispatched.taskPath,
    manifestPath: dispatched.manifestPath,
    initPath: dispatched.initPath,
    relayPrompt: dispatched.relayPrompt,
    assigneeRoleId: dispatched.assigneeRoleId,
    executionSessionId: dispatched.executionSessionId,
    requester: taskAfter.requester,
    state: taskAfter.state,
    session,
    // Prefer envelope projection (Role baseCommit only after claim; Connection-downstream may
    // still carry dispatch-time lane). Fall back to in-memory lane only when present.
    workspaceLane: projectTask(taskAfter).workspaceLane
  };
}
async function assertDownstreamDispatchPreconditions(fs21, input) {
  if (input.requester.kind !== "role") {
    throw new RpcError(
      -32602,
      "task.dispatch downstream requires requester kind=role naming a real durable registry role (not user)",
      { requester: input.requester }
    );
  }
  const dispatcher = input.requester.id.trim();
  if (!dispatcher || dispatcher === "user") {
    throw new RpcError(
      -32602,
      "task.dispatch downstream requires requester naming a real durable registry role (not user)"
    );
  }
  if (input.targetRoleId && dispatcher === input.targetRoleId) {
    throw new RpcError(
      -32602,
      "task.dispatch downstream requester must not equal the assignee itself",
      { requester: input.requester, roleId: input.targetRoleId }
    );
  }
  const registry = await loadRolesRegistry(fs21);
  const role = registry.roles.find((item) => item.id === dispatcher);
  if (!role) {
    throw new RpcError(
      -32602,
      `task.dispatch downstream requester role not found in registry: ${dispatcher}`,
      { requester: input.requester }
    );
  }
}
function parseOptionalTaskActor(value, label) {
  if (value === void 0 || value === null) return void 0;
  try {
    return parseTaskActorRef(value, label);
  } catch (err) {
    throw new RpcError(
      -32602,
      err instanceof Error ? err.message : `Invalid ${label}`,
      { field: label }
    );
  }
}
function taskIsDownstream(task) {
  return task.requester?.kind === "role" && task.requester.id !== task.assigneeRoleId;
}
function resolveTaskNodeSelection(p) {
  try {
    return { nodeIds: orderedTaskNodeIds(normalizeTaskNodeSelection({ nodeIds: p.nodeIds })) };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(-32602, message2, { field: "nodeIds" });
  }
}
function resolveDispatchRequesterFromRpc(requester) {
  if (!requester) {
    throw new RpcError(
      -32602,
      "task.dispatch requires explicit requester { kind: user|role, id }"
    );
  }
  return requester;
}
async function taskClaimDirectRpc(ctx, p, caller = {}) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set([
      "workspaceId",
      "roleId",
      "nodeIds",
      "prompt",
      "sourceTaskPath"
    ]),
    "task.claimDirect"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const roleId = requireString(p, "roleId");
  const prompt = requireString(p, "prompt");
  const sourceTaskPath = optionalString2(p, "sourceTaskPath");
  const selection = resolveTaskNodeSelection(p);
  return ctx.mutations.run(workspaceId, async () => {
    const registry = await loadRolesRegistry(mount.env.fs);
    const roleDefinition = registry.roles.find((role) => role.id === roleId);
    if (!roleDefinition) {
      throw new RpcError(-32004, `Role not found in registry: ${roleId}`, {
        code: "DIRECT_CLAIM_ROLE_NOT_FOUND",
        roleId
      });
    }
    const callerSession = await requireRoleClaimCallerSession(
      ctx,
      workspaceId,
      roleId,
      caller
    );
    await assertRoleSessionHasNoOtherActiveTask(
      ctx,
      workspaceId,
      callerSession.id
    );
    const requester = await resolveDirectClaimResponsibility(ctx, {
      workspaceId,
      roleId,
      sourceTaskPath,
      sourceSessionId: callerSession.id
    });
    await ensureRoleWorkspaceIfGit(mount.workspaceRoot, roleDefinition.name);
    const settings = await loadWorkspaceSettings(mount.env.fs);
    const acceptMode = allowsNonReviewAcceptMode({
      requester
    }) ? settings.defaultAcceptMode : "review-required";
    ctx.host.markSelfWrite(workspaceId);
    const roleRootPath = roleTempRoot(roleId);
    const expectedInitPath = nodePath6.posix.join(roleRootPath, "init.md");
    const initExisted = await mount.env.fs.exists(expectedInitPath);
    const initBefore = initExisted ? await mount.env.fs.readFile(expectedInitPath) : void 0;
    let created;
    let previousLastTaskId;
    let callerSessionRebound = false;
    try {
      created = await dispatch(mount.env, {
        prompt,
        requester,
        acceptMode,
        assigneeRoleId: roleId,
        executionSessionId: callerSession.id,
        nodeIds: selection.nodeIds
      });
      const pre = await loadTaskRecord(mount.env.fs, created.taskPath);
      previousLastTaskId = callerSession.currentTaskId;
      await ctx.runtime.registry.update(callerSession.id, {
        currentTaskId: pre.id || pre.path
      });
      callerSessionRebound = true;
      const claimWrite = await prepareRoleClaimWrite(ctx, workspaceId, pre);
      if (beforeTaskClaimCoreForTests) {
        await beforeTaskClaimCoreForTests({
          workspaceId,
          taskPath: created.taskPath,
          task: pre
        });
      }
      const task = await taskClaim(mount.env, created.taskPath, {
        ...claimWrite ? { claimWrite } : {}
      });
      emitTaskState(ctx, workspaceId, task, "task.claimDirect");
      for (const nodeId of task.nodeIds) {
        if (nodeId === "root") continue;
        ctx.events.emit(
          "node.changed",
          workspaceId,
          { nodeId, reason: "task.claim-projection" },
          "self"
        );
      }
      return {
        workspaceId,
        taskPath: created.taskPath,
        manifestPath: created.manifestPath,
        initPath: created.initPath,
        task: projectTask(task),
        state: task.state,
        roleId: task.assigneeRoleId,
        sessionId: task.executionSessionId,
        nodeIds: [...task.nodeIds]
      };
    } catch (err) {
      if (callerSessionRebound) {
        await ctx.runtime.registry.update(callerSession.id, { currentTaskId: previousLastTaskId }).catch(() => void 0);
      }
      if (created) {
        const cleanupErrors = [];
        for (const exactPath of [created.taskPath, created.manifestPath]) {
          try {
            if (await mount.env.fs.exists(exactPath)) {
              await mount.env.fs.remove(exactPath);
            }
          } catch (cleanupErr) {
            cleanupErrors.push(
              `${exactPath}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
            );
          }
        }
        if (created.initPath) {
          try {
            if (initExisted && initBefore !== void 0) {
              await mount.env.fs.writeFile(created.initPath, initBefore);
            } else if (await mount.env.fs.exists(created.initPath)) {
              await mount.env.fs.remove(created.initPath);
            }
          } catch (cleanupErr) {
            cleanupErrors.push(
              `${created.initPath}: ${cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)}`
            );
          }
        }
        if (cleanupErrors.length > 0) {
          throw new RpcError(
            RPC_LIFECYCLE,
            `task.claimDirect failed and exact artifact cleanup was incomplete: ${cleanupErrors.join("; ")}`,
            {
              code: "DIRECT_CLAIM_CLEANUP_FAILED",
              taskPath: created.taskPath,
              cause: err instanceof Error ? err.message : String(err)
            }
          );
        }
      }
      throw err;
    }
  });
}
async function resolveDirectClaimResponsibility(ctx, input) {
  const mount = ctx.host.require(input.workspaceId);
  let session = null;
  if (input.sourceSessionId) {
    session = await ctx.runtime.registry.read(input.sourceSessionId);
    if (!session) {
      throw new RpcError(-32004, `Session not found: ${input.sourceSessionId}`, {
        code: "DIRECT_CLAIM_SESSION_NOT_FOUND"
      });
    }
    if (session.workspace !== input.workspaceId || session.roleId !== input.roleId || session.state !== "external" || !SessionRegistry.isOpen(session.state)) {
      throw new RpcError(
        -32001,
        "task.claimDirect source Session is not a live exact-workspace binding for the claiming Role",
        {
          code: "DIRECT_CLAIM_SESSION_MISMATCH",
          sessionId: session.id,
          roleId: input.roleId
        }
      );
    }
  }
  const rootResponsibility = () => ({ kind: "user", id: "user" });
  const explicitSourceTask = Boolean(input.sourceTaskPath);
  const sourceRef = input.sourceTaskPath || session?.currentTaskId?.trim() || "";
  if (!sourceRef) {
    return rootResponsibility();
  }
  let sourceTask;
  if (input.sourceTaskPath) {
    try {
      sourceTask = await loadTaskRecord(mount.env.fs, input.sourceTaskPath);
    } catch (err) {
      throw new RpcError(
        -32004,
        `task.claimDirect source Task could not be loaded: ${err instanceof Error ? err.message : String(err)}`,
        { code: "DIRECT_CLAIM_SOURCE_TASK_NOT_FOUND", taskPath: input.sourceTaskPath }
      );
    }
  } else {
    const matches = (await loadTaskRecords(mount.env.fs)).filter(
      (task) => task.id === sourceRef || task.path === sourceRef
    );
    if (matches.length === 0) {
      return rootResponsibility();
    }
    if (matches.length !== 1) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.claimDirect Session currentTaskId resolved ambiguously (${matches.length} Tasks)`,
        { code: "DIRECT_CLAIM_SOURCE_TASK_AMBIGUOUS", currentTaskId: sourceRef }
      );
    }
    sourceTask = matches[0];
  }
  const sameRoleTask = sourceTask.assigneeRoleId === input.roleId;
  const activeClaim = sourceTask.state !== "queued" && isActiveTaskState(sourceTask.state);
  if (explicitSourceTask && (!sameRoleTask || !activeClaim)) {
    throw new RpcError(
      -32001,
      "task.claimDirect source Task is not an active claimed Task owned by the claiming Role",
      {
        code: "DIRECT_CLAIM_SOURCE_TASK_MISMATCH",
        taskPath: sourceTask.path,
        roleId: input.roleId,
        taskRoleId: sourceTask.assigneeRoleId,
        taskSessionId: sourceTask.executionSessionId,
        state: sourceTask.state
      }
    );
  }
  if (!explicitSourceTask && !sameRoleTask) {
    throw new RpcError(
      -32001,
      "task.claimDirect Session currentTaskId belongs to a different Role or assignee kind",
      {
        code: "DIRECT_CLAIM_SOURCE_TASK_MISMATCH",
        taskPath: sourceTask.path,
        roleId: input.roleId,
        taskRoleId: sourceTask.assigneeRoleId,
        taskSessionId: sourceTask.executionSessionId
      }
    );
  }
  if (!explicitSourceTask && sourceTask.state === "queued") {
    return rootResponsibility();
  }
  if (!explicitSourceTask && session && isActiveTaskState(sourceTask.state) && sourceTask.executionSessionId && sourceTask.executionSessionId !== session.id) {
    return rootResponsibility();
  }
  if (!sourceTask.requester) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "task.claimDirect source Task is missing persisted requester",
      { code: "DIRECT_CLAIM_SOURCE_AUTHORITY_MISSING", taskPath: sourceTask.path }
    );
  }
  return sourceTask.requester;
}
async function taskClaimRpc(ctx, p, caller = {}) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath"]),
    "task.claim"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  return runTaskLifecycle(
    workspaceId,
    taskPath,
    () => ctx.mutations.run(workspaceId, async () => {
      const pre = await loadTaskRecord(mount.env.fs, taskPath);
      let claimWrite;
      let callerSession;
      let previousLastTaskId;
      let recoverWaitingExternalRoleTask = false;
      if (pre.assigneeRoleId) {
        callerSession = await requireRoleClaimCallerSession(
          ctx,
          workspaceId,
          pre.assigneeRoleId,
          caller
        );
        await assertRoleSessionHasNoOtherActiveTask(
          ctx,
          workspaceId,
          callerSession.id,
          { path: pre.path, id: pre.id }
        );
        if (pre.state === "running" && pre.executionSessionId !== callerSession.id) {
          throw new RpcError(
            RPC_LIFECYCLE,
            "task.claim cannot replace the exact Session of a running Role Task",
            {
              code: "TASK_CLAIM_SESSION_MISMATCH",
              taskPath,
              boundSessionId: pre.executionSessionId,
              callerSessionId: callerSession.id
            }
          );
        }
        recoverWaitingExternalRoleTask = pre.state === "waiting";
      }
      if (pre.state !== "running" && !recoverWaitingExternalRoleTask) {
        const preparedRoleWrite = await prepareRoleClaimWrite(ctx, workspaceId, pre);
        claimWrite = {
          ...preparedRoleWrite ?? {},
          ...callerSession ? { executionSessionId: callerSession.id } : {}
        };
        if (beforeTaskClaimCoreForTests) {
          await beforeTaskClaimCoreForTests({ workspaceId, taskPath, task: pre });
        }
      }
      if (callerSession && pre.state !== "running" && !recoverWaitingExternalRoleTask) {
        previousLastTaskId = callerSession.currentTaskId;
        await ctx.runtime.registry.update(callerSession.id, {
          currentTaskId: pre.id || pre.path
        });
      }
      let task;
      try {
        if (pre.state !== "running" && !recoverWaitingExternalRoleTask) {
          ctx.host.markSelfWrite(workspaceId);
        }
        task = recoverWaitingExternalRoleTask ? await restoreExactExternalRoleTask(ctx, {
          workspaceId,
          taskPath,
          callerSessionId: callerSession.id
        }) : await taskClaim(mount.env, taskPath, {
          ...claimWrite ? { claimWrite } : {}
        });
      } catch (error) {
        if (callerSession && pre.state !== "running" && !recoverWaitingExternalRoleTask) {
          await ctx.runtime.registry.update(callerSession.id, { currentTaskId: previousLastTaskId }).catch(() => void 0);
        }
        throw error;
      }
      emitTaskState(ctx, workspaceId, task, "task.claim");
      for (const nodeId of task.nodeIds) {
        if (nodeId === "root") continue;
        ctx.events.emit(
          "node.changed",
          workspaceId,
          { nodeId, reason: "task.claim-projection" },
          "self"
        );
      }
      return {
        workspaceId,
        taskPath,
        task: projectTask(task),
        state: task.state,
        assigneeRoleId: task.assigneeRoleId,
        nodeIds: [...task.nodeIds],
        executionSessionId: task.executionSessionId
      };
    })
  );
}
async function restoreExactExternalRoleTask(ctx, input) {
  const mount = ctx.host.require(input.workspaceId);
  const task = await loadTaskRecord(mount.env.fs, input.taskPath);
  const roleId = task.assigneeRoleId?.trim() || "";
  const sessionId = task.executionSessionId?.trim() || "";
  if (!roleId || !sessionId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "External Role Task recovery requires exact persisted roleId and sessionId",
      { code: "EXTERNAL_ROLE_TASK_BINDING_REQUIRED", taskPath: input.taskPath }
    );
  }
  if (input.callerSessionId && input.callerSessionId !== sessionId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "task.claim caller Session does not own the exact waiting Role Task",
      {
        code: "TASK_CLAIM_SESSION_MISMATCH",
        taskPath: input.taskPath,
        boundSessionId: sessionId,
        callerSessionId: input.callerSessionId
      }
    );
  }
  const session = await ctx.runtime.registry.read(sessionId);
  const lastTaskRef = session?.currentTaskId?.trim() || "";
  if (!session || session.state !== "external" || session.adapterId !== EXTERNAL_ADAPTER_ID || session.workspace !== input.workspaceId || session.roleId !== roleId || lastTaskRef !== task.id && lastTaskRef !== task.path) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "External Role Session is not bound to the exact workspace/Role/Task",
      {
        code: "EXTERNAL_ROLE_TASK_SESSION_MISMATCH",
        taskPath: input.taskPath,
        sessionId,
        roleId
      }
    );
  }
  const probe = await ctx.runtime.probe(sessionId);
  if (!probe.isAlive || probe.state !== "external" || !SessionRegistry.isOpen(probe.state)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "External Role Session is not alive and open for exact Task recovery",
      {
        code: "EXTERNAL_ROLE_TASK_SESSION_UNAVAILABLE",
        taskPath: input.taskPath,
        sessionId,
        state: probe.state
      }
    );
  }
  if (task.state === "running") return task;
  if (task.state !== "waiting" || !isRejectResumeParkedWait(task) && !isSessionUnavailableParkedWait(task)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "External Role Task is not in an exact recoverable Session wait",
      {
        code: "EXTERNAL_ROLE_TASK_WAIT_NOT_RECOVERABLE",
        taskPath: input.taskPath,
        state: task.state,
        waitCode: task.wait?.code
      }
    );
  }
  ctx.host.markSelfWrite(input.workspaceId);
  return taskResume(mount.env, input.taskPath);
}
async function requireRoleClaimCallerSession(ctx, workspaceId, roleId, caller, action = "task.claim") {
  let callerSessionId = caller.sessionId?.trim() || void 0;
  if (!callerSessionId && caller.externalKey?.trim()) {
    const matches = (await ctx.runtime.registry.list()).filter(
      (record) => record.state === "external" && recordExternalKey(record) === caller.externalKey.trim()
    );
    if (matches.length === 1) callerSessionId = matches[0].id;
    else if (matches.length > 1) {
      throw new RpcError(-32001, `${action} host-native Session context is ambiguous`, {
        code: "TASK_CLAIM_CALLER_SESSION_AMBIGUOUS",
        externalKey: caller.externalKey
      });
    }
  }
  if (!callerSessionId) {
    throw new RpcError(
      -32001,
      `${action} requires trusted current Role Session context`,
      { code: "TASK_CLAIM_CALLER_SESSION_REQUIRED", roleId }
    );
  }
  const mount = ctx.host.require(workspaceId);
  await requireRoleNameById(mount.env.fs, roleId);
  const session = await ctx.runtime.registry.read(callerSessionId);
  if (!session || session.state !== "external" || session.workspace !== workspaceId || session.roleId !== roleId) {
    throw new RpcError(
      -32001,
      `${action} caller Session is not an exact live workspace/Role binding`,
      {
        code: "TASK_CLAIM_CALLER_SESSION_MISMATCH",
        roleId,
        callerSessionId
      }
    );
  }
  return session;
}
async function assertRoleSessionHasNoOtherActiveTask(ctx, workspaceId, sessionId, excludeTask) {
  const conflictingTasks = (await listIncompleteTasksBoundToSession(ctx, workspaceId, sessionId)).filter(
    (task) => !excludeTask || task.path !== excludeTask.path && (!excludeTask.id || task.id !== excludeTask.id)
  ).sort((a, b) => a.path.localeCompare(b.path));
  if (conflictingTasks.length === 0) return;
  const conflict = conflictingTasks[0];
  throw new RpcError(
    RPC_LIFECYCLE,
    "task.claim caller Session is already bound to another active Task",
    {
      code: "TASK_CLAIM_SESSION_ALREADY_ACTIVE",
      sessionId,
      conflictingTaskPath: conflict.path,
      conflictingTaskId: conflict.id,
      conflictingTaskState: conflict.state
    }
  );
}
async function prepareRoleClaimWrite(ctx, workspaceId, task) {
  if (!task.assigneeRoleId) return void 0;
  const mount = ctx.host.require(workspaceId);
  if (!await isGitWorkspace(mount.workspaceRoot)) return void 0;
  if (task.baseCommit?.trim() && task.baseCommitCapture) return void 0;
  if (!task.requester) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.claim Role capture requires exact persisted requester (task ${task.id || task.path})`,
      { taskPath: task.path, code: "BASE_CAPTURE_ACTOR" }
    );
  }
  let real;
  try {
    const registry = await loadRolesRegistry(mount.env.fs);
    let targetBranchHint;
    if (taskIsDownstream(task)) {
      const dispatcher = taskParentRoleId(task);
      if (!dispatcher) {
        throw new Error(
          `Sub task ${task.id || task.path} is missing a durable parent Role for claim lane bind.`
        );
      }
      const dispatcherRole = registry.roles.find((item) => item.id === dispatcher);
      if (!dispatcherRole) throw new Error(`Parent Role not found in registry: ${dispatcher}`);
      const dispatcherLane = await ensureRoleWorkspace(
        mount.workspaceRoot,
        dispatcherRole.name
      );
      targetBranchHint = dispatcherLane.branch;
      const recordedTarget = task.targetBranch?.trim();
      if (recordedTarget && recordedTarget !== targetBranchHint) {
        throw new Error(
          `Task envelope targetBranch mismatch for Role ${task.assigneeRoleId}: envelope=${recordedTarget} expected=${targetBranchHint}`
        );
      }
    }
    const role = registry.roles.find((item) => item.id === task.assigneeRoleId);
    if (!role) throw new Error(`Role not found in registry: ${task.assigneeRoleId}`);
    const ensured = await ensureRoleWorkspace(mount.workspaceRoot, role.name);
    const view = {
      ...task,
      workspace: task.workspace || ensured.workspace,
      worktree: task.worktree || ensured.worktree,
      branch: task.branch || ensured.branch,
      targetBranch: task.targetBranch || targetBranchHint || ensured.targetBranch
    };
    real = await resolveIntegrationContract(mount.workspaceRoot, view, mount.env.fs);
  } catch (err) {
    throw new RpcError(
      RPC_LIFECYCLE,
      err instanceof Error ? `task.claim Role lane prepare failed: ${err.message}` : "task.claim Role lane prepare failed",
      { taskPath: task.path, code: "BASE_CAPTURE_LANE" }
    );
  }
  const now = mount.env.clock.now();
  const patch = {
    workspace: real.workspace,
    worktree: real.worktree,
    branch: real.branch,
    targetBranch: real.targetBranch,
    updatedAt: now
  };
  const existingBase = task.baseCommit?.trim() || "";
  if (existingBase) {
    let fullExisting;
    try {
      fullExisting = await resolveCommitSha(real.workspace, existingBase);
    } catch (err) {
      throw new RpcError(
        RPC_LIFECYCLE,
        err instanceof Error ? `task.claim Role baseCommit unresolvable in lane repo: ${err.message}` : "task.claim Role baseCommit unresolvable in lane repo",
        {
          taskPath: task.path,
          baseCommit: existingBase,
          code: "BASE_CAPTURE_UNRESOLVED"
        }
      );
    }
    patch.baseCommit = fullExisting;
    if (!task.baseCommitCapture) {
      patch.baseCommitCapture = {
        source: "first-claim",
        baseCommit: fullExisting,
        actor: task.requester,
        capturedAt: now
      };
    }
    return patch;
  }
  const tip = typeof real.baseCommit === "string" && real.baseCommit.trim() ? real.baseCommit.trim() : await readRoleBranchTip(real.workspace, real.branch);
  const fullTip = await resolveCommitSha(real.workspace, tip);
  patch.baseCommit = fullTip;
  if (!task.roleBranchBase?.trim()) {
    patch.roleBranchBase = fullTip;
  }
  patch.baseCommitCapture = {
    source: "first-claim",
    baseCommit: fullTip,
    actor: task.requester,
    capturedAt: now
  };
  if (!task.integrationAuthority) {
    patch.integrationAuthority = deriveIntegrationAuthority({ requester: task.requester });
  }
  return patch;
}
async function taskWaitRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const reason = requireString(p, "reason");
  const summary = requireString(p, "summary");
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const task = await taskWait(mount.env, taskPath, { reason, summary });
    emitTaskState(ctx, workspaceId, task, "task.wait");
    return { workspaceId, taskPath, task: projectTask(task), state: task.state };
  });
}
async function taskResumeRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const resumed = await runTaskLifecycle(
    workspaceId,
    taskPath,
    () => ctx.mutations.run(workspaceId, async () => {
      await reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath);
      ctx.host.markSelfWrite(workspaceId);
      const task = await taskResume(mount.env, taskPath);
      emitTaskState(ctx, workspaceId, task, "task.resume");
      return task;
    })
  );
  const sessionId = resumed.executionSessionId?.trim();
  if (sessionId) {
    try {
      await requestManagedAutoSubmitRetryFromDraft(ctx, {
        workspaceId,
        taskPath,
        sessionId
      });
    } catch {
    }
  }
  const current = (await runTaskLifecycle(
    workspaceId,
    taskPath,
    () => ctx.mutations.run(workspaceId, () => reconcileTaskLifecycle(mount.env, taskPath))
  )).task;
  return { workspaceId, taskPath, task: projectTask(current), state: current.state };
}
async function resolveDecisionCallerSession(ctx, caller) {
  const sessionId = caller.callerSessionId?.trim();
  if (sessionId) {
    const session = await ctx.runtime.registry.read(sessionId);
    if (!session) {
      throw new RpcError(-32001, "Authenticated caller Session is not registered", {
        code: "DECISION_CALLER_SESSION_MISSING",
        sessionId
      });
    }
    return session;
  }
  return void 0;
}
function assertDecisionRequesterBinding(task, workspaceId, session) {
  if (!task.id || !task.executionSessionId || !session) {
    throw new RpcError(
      -32001,
      "task.requestDecision requires the exact authenticated executing Session",
      { code: "DECISION_REQUESTER_SESSION_REQUIRED", taskPath: task.path }
    );
  }
  if (session.id !== task.executionSessionId || session.workspace !== workspaceId || session.currentTaskId !== task.id || !SessionRegistry.isOpen(session.state)) {
    throw new RpcError(
      -32001,
      "Decision requester is not the exact live Session bound to this Task",
      {
        code: "DECISION_REQUESTER_SESSION_MISMATCH",
        taskId: task.id,
        taskSessionId: task.executionSessionId,
        callerSessionId: session.id
      }
    );
  }
}
async function taskRequestDecisionRpc(ctx, p, caller) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath", "question", "options"]),
    "task.requestDecision"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const question = requireString(p, "question").trim();
  if (!question) {
    throw new RpcError(-32602, "task.requestDecision requires non-empty question");
  }
  const options = parseDecisionRequestOptions(p.options);
  const existing = await ctx.decisionRequests.getPendingForTask(workspaceId, taskPath);
  if (existing) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Task already has a pending Decision Request (${existing.id})`,
      { requestId: existing.id, workspaceId, taskPath }
    );
  }
  return runTaskLifecycle(workspaceId, taskPath, () => ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const current = await loadTaskRecord(mount.env.fs, taskPath);
    if (current.state !== "running") {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.requestDecision requires running task (state=${current.state})`,
        { taskPath, state: current.state }
      );
    }
    const callerSession = await resolveDecisionCallerSession(ctx, caller);
    assertDecisionRequesterBinding(current, workspaceId, callerSession);
    const again = await ctx.decisionRequests.getPendingForTask(workspaceId, taskPath);
    if (again) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Task already has a pending Decision Request (${again.id})`,
        { requestId: again.id, workspaceId, taskPath }
      );
    }
    if (!current.requester) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "task.requestDecision requires a frozen parent authority",
        { code: "DECISION_TARGET_MISSING", taskId: current.id }
      );
    }
    const request = {
      id: makeDecisionRequestId(),
      taskId: current.id,
      requester: { kind: "session", id: callerSession.id },
      target: current.requester,
      question,
      options,
      status: "pending"
    };
    const task = await taskWait(mount.env, taskPath, {
      reason: "user-input",
      summary: `Decision Request pending: ${question.slice(0, 200)}`,
      code: `decision_request:${request.id}`
    });
    let stored;
    try {
      stored = await ctx.decisionRequests.add({ workspaceId, taskPath, request });
    } catch (error) {
      await taskResume(mount.env, taskPath);
      throw error;
    }
    emitTaskState(ctx, workspaceId, task, "task.requestDecision");
    ctx.events.emit(
      "decisionRequest.pending",
      workspaceId,
      projectDecisionRequest(stored),
      "self"
    );
    if (callerSession.state !== "external") {
      try {
        await ctx.runtime.registry.update(callerSession.id, { state: "waiting-user" });
      } catch {
      }
    }
    return {
      workspaceId,
      taskPath,
      task: projectTask(task),
      state: task.state,
      request: projectDecisionRequest(stored)
    };
  }));
}
function parseDecisionRequestOptions(raw) {
  if (raw === void 0 || raw === null) return [];
  if (!Array.isArray(raw)) {
    throw new RpcError(-32602, "task.requestDecision options must be an array");
  }
  const options = [];
  for (const item of raw) {
    if (!item || typeof item !== "object" || Array.isArray(item)) {
      throw new RpcError(-32602, "task.requestDecision option must be {id,label}");
    }
    const row = item;
    assertAllowedParams(row, /* @__PURE__ */ new Set(["id", "label"]), "task.requestDecision option");
    const id = typeof row.id === "string" ? row.id.trim() : "";
    const label = typeof row.label === "string" ? row.label.trim() : "";
    if (!id || !label) {
      throw new RpcError(-32602, "task.requestDecision option requires non-empty id and label");
    }
    options.push({ id, label });
  }
  return options;
}
function taskRejectResumeFeedbackId(resultId) {
  if (!/^rs-[a-z0-9]+$/i.test(resultId)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Reject-resume continuation requires a canonical TaskResult id: ${resultId}`,
      { code: "TASK_REJECT_RESUME_CONTINUATION_CHANGED", resultId }
    );
  }
  return `ti-rf-${resultId.slice(3).toLowerCase()}`;
}
function rejectResumeFeedbackMatches(item, input) {
  const expectedSessionId = input.task.executionSessionId?.trim() || void 0;
  const actualSessionId = item.sessionId?.trim() || void 0;
  const expectedText = input.exactText ? input.note : input.note.trim() || DEFAULT_TASK_REJECT_NOTE;
  const actualText = input.exactText ? item.text ?? "" : (item.text ?? "").trim() || DEFAULT_TASK_REJECT_NOTE;
  const requiresCurrentSession = item.status === "pending" || item.status === "processing" || item.status === "failed";
  return item.id === taskRejectResumeFeedbackId(input.resultId) && item.workspaceId === input.workspaceId && item.taskPath === input.taskPath && item.taskId === input.task.id && (!requiresCurrentSession || actualSessionId === expectedSessionId) && item.role === taskParentRoleId(input.task) && normalizeTaskInputKind(item.kind) === "review-feedback" && typeof item.text === "string" && actualText === expectedText;
}
async function reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath, options) {
  const mount = ctx.host.require(workspaceId);
  const reconciled = await reconcileTaskLifecycle(mount.env, taskPath);
  const pending = reconciled.rejectResume;
  if (!pending) return reconciled.task;
  const inputId = taskRejectResumeFeedbackId(pending.resultId);
  const item = await ctx.taskInputs.get(inputId, workspaceId, taskPath);
  if (!item || !rejectResumeFeedbackMatches(item, {
    workspaceId,
    taskPath,
    task: reconciled.task,
    resultId: pending.resultId,
    note: pending.note,
    exactText: false
  })) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Reject-resume continuation is incomplete; retry the exact task.reject request before mutating this Task.",
      {
        code: "TASK_REJECT_RESUME_CONTINUATION_REQUIRED",
        taskPath,
        resultId: pending.resultId,
        inputId
      }
    );
  }
  if (item.status === "uncertain") {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Reject-resume review feedback has uncertain result; acknowledge it with taskInput.ack before starting or replacing the Session.",
      {
        code: "TASK_REJECT_RESUME_CONTINUATION_UNCERTAIN",
        taskPath,
        resultId: pending.resultId,
        inputId
      }
    );
  }
  if (item.status === "cancelled") {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Reject-resume review feedback was cancelled; retry the exact task.reject request before mutating this Task.",
      {
        code: "TASK_REJECT_RESUME_CONTINUATION_CANCELLED",
        taskPath,
        resultId: pending.resultId,
        inputId
      }
    );
  }
  if (options?.blockOpenRejectResumeFeedback && (item.status === "pending" || item.status === "processing" || item.status === "failed")) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Reject-resume review feedback must run before new task input; retry the exact task.reject request if recovery is required.",
      {
        code: "TASK_REJECT_RESUME_CONTINUATION_OPEN",
        taskPath,
        resultId: pending.resultId,
        inputId,
        inputStatus: item.status
      }
    );
  }
  return reconciled.task;
}
function reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath, options) {
  return ctx.mutations.run(
    workspaceId,
    () => reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath, options)
  );
}
async function taskSendInputRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const actorRaw = optionalString2(p, "actor") ?? "user";
  if (actorRaw !== "user") {
    throw new RpcError(
      -32001,
      "task.sendInput is user-only; agent self-send is forbidden",
      { actor: actorRaw }
    );
  }
  const textRaw = optionalString2(p, "text");
  const text3 = textRaw?.trim() ?? "";
  const contextRefs = parseContextRefs(p.contextRefs);
  if (!text3 && contextRefs.length === 0) {
    throw new RpcError(
      -32602,
      "task.sendInput requires non-empty text and/or contextRefs"
    );
  }
  const pendingDecision = await ctx.decisionRequests.getPendingForTask(workspaceId, taskPath);
  if (pendingDecision) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Task has a pending Decision Request (${pendingDecision.id}); use decisionRequest.respond instead of task.sendInput`,
      { requestId: pendingDecision.id, workspaceId, taskPath }
    );
  }
  const { current, input } = await runTaskLifecycle(workspaceId, taskPath, () => ctx.mutations.run(workspaceId, async () => {
    const current2 = await reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath, {
      blockOpenRejectResumeFeedback: true
    });
    if (current2.state !== "running" && current2.state !== "waiting") {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.sendInput requires running or waiting task (state=${current2.state})`,
        { taskPath, state: current2.state }
      );
    }
    const currentDecision = await ctx.decisionRequests.getPendingForTask(
      workspaceId,
      taskPath
    );
    if (currentDecision) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Task has a pending Decision Request (${currentDecision.id}); use decisionRequest.respond instead of task.sendInput`,
        { requestId: currentDecision.id, workspaceId, taskPath }
      );
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const input2 = await ctx.taskInputs.add({
      id: makeTaskInputId(),
      workspaceId,
      taskPath,
      taskId: current2.id || void 0,
      sessionId: current2.executionSessionId || void 0,
      role: taskParentRoleId(current2),
      kind: "user-input",
      ...text3 ? { text: text3 } : {},
      ...contextRefs.length > 0 ? { contextRefs } : {},
      status: "pending",
      createdAt: now,
      updatedAt: now
    });
    return { current: current2, input: input2 };
  }));
  ctx.events.emit(
    "taskInput.pending",
    workspaceId,
    {
      inputId: input.id,
      taskPath: input.taskPath,
      taskId: input.taskId,
      sessionId: input.sessionId,
      role: input.role,
      kind: normalizeTaskInputKind(input.kind),
      text: input.text,
      contextRefs: input.contextRefs,
      createdAt: input.createdAt
    },
    "self"
  );
  const hasManagedSession = !!current.executionSessionId?.trim();
  if (hasManagedSession) {
    enqueueManagedTaskInputBackground(ctx, input);
  }
  return {
    workspaceId,
    taskPath,
    task: projectTask(current),
    state: current.state,
    input: projectTaskInput(input),
    /** Durable row accepted; does not mean provider turn finished. */
    accepted: true,
    /** True when a managed session is bound and background inject was scheduled. */
    enqueued: hasManagedSession,
    /** Always false on accept — poll taskInput.get / events for delivered|failed. */
    continued: false
  };
}
function parseContextRefs(raw) {
  if (raw === void 0 || raw === null) return [];
  if (!Array.isArray(raw)) {
    throw new RpcError(-32602, "task.sendInput contextRefs must be an array");
  }
  const refs = [];
  const seen = /* @__PURE__ */ new Set();
  for (const item of raw) {
    if (typeof item !== "string" || !item.trim()) {
      throw new RpcError(
        -32602,
        "task.sendInput contextRefs must be non-empty strings (stable entity ids)"
      );
    }
    const id = item.trim();
    if (seen.has(id)) continue;
    seen.add(id);
    refs.push(id);
  }
  return refs;
}
var managedTaskInputQueue = new MutationBus();
var managedTaskInputBackgroundInflight = /* @__PURE__ */ new Set();
var managedTaskInputAccepting = true;
function managedTaskInputQueueKey(workspaceId, taskPath) {
  return `${workspaceId}\0${taskPath}`;
}
var managedTaskInputQueueHoldForTests = /* @__PURE__ */ new Map();
async function resolveManagedInjectSessionId(ctx, latest) {
  const mount = ctx.host.get(latest.workspaceId);
  if (!mount) {
    throw new Error(`TaskInput workspace is not mounted: ${latest.workspaceId}`);
  }
  const task = await loadTaskRecord(mount.env.fs, latest.taskPath);
  if (!task.id) {
    throw new Error(`TaskInput exact Task is missing canonical id: ${latest.taskPath}`);
  }
  if (latest.taskId && task.id !== latest.taskId) {
    throw new Error(
      `TaskInput Task identity mismatch: expected ${latest.taskId}, got ${task.id}`
    );
  }
  const sessionId = task.executionSessionId?.trim();
  if (!sessionId) return void 0;
  const session = await ctx.runtime.registry.read(sessionId);
  if (!session) {
    throw new Error(`TaskInput bound Session is missing from registry: ${sessionId}`);
  }
  if (session.workspace !== latest.workspaceId) {
    throw new Error(
      `TaskInput bound Session workspace mismatch: expected ${latest.workspaceId}, got ${session.workspace || "missing"}`
    );
  }
  if (session.currentTaskId !== task.id) {
    throw new Error(
      `TaskInput bound Session Task mismatch: expected ${task.id}, got ${session.currentTaskId || "missing"}`
    );
  }
  return sessionId;
}
function trackManagedTaskInputBackground(work) {
  managedTaskInputBackgroundInflight.add(work);
  void work.then(
    () => {
      managedTaskInputBackgroundInflight.delete(work);
    },
    (err) => {
      managedTaskInputBackgroundInflight.delete(work);
      const message2 = err instanceof Error ? err.message : String(err);
      console.error(`[taskInput] background managed injection failed: ${message2}`);
    }
  );
}
function enqueueManagedTaskInputBackground(ctx, item) {
  if (!managedTaskInputAccepting) {
    return;
  }
  const work = injectManagedTaskInput(ctx, item).then(
    () => void 0,
    async (err) => {
      const message2 = err instanceof Error ? err.message : String(err);
      try {
        const latest = await ctx.taskInputs.get(
          item.id,
          item.workspaceId,
          item.taskPath
        );
        if (latest && (latest.status === "pending" || latest.status === "processing" || latest.status === "failed")) {
          await ctx.taskInputs.markFailed(item.id, message2, "service");
        }
      } catch {
      }
      throw err;
    }
  );
  trackManagedTaskInputBackground(work);
}
async function scheduleRetryableTaskInputsAfterSessionBind(ctx, input) {
  const retryable = await ctx.taskInputs.listRetryableForTask(
    input.workspaceId,
    input.taskPath
  );
  retryable.sort(
    (a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id)
  );
  for (const item of retryable) {
    enqueueManagedTaskInputBackground(ctx, item);
  }
}
function stopManagedTaskInputBackgroundAccept() {
  managedTaskInputAccepting = false;
}
async function drainManagedTaskInputBackgroundForShutdown(timeoutMs = 5e3) {
  managedTaskInputAccepting = false;
  const pending = [...managedTaskInputBackgroundInflight];
  if (pending.length === 0) return;
  const bound = typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : 5e3;
  if (bound === 0) {
    await Promise.allSettled(pending);
    return;
  }
  let timer;
  try {
    await Promise.race([
      Promise.allSettled(pending),
      new Promise((resolve15) => {
        timer = setTimeout(resolve15, bound);
      })
    ]);
  } finally {
    if (timer !== void 0) clearTimeout(timer);
  }
}
function enableManagedTaskInputBackgroundAccept() {
  managedTaskInputAccepting = true;
}
async function injectManagedTaskInput(ctx, item) {
  const queueKey = managedTaskInputQueueKey(item.workspaceId, item.taskPath);
  return managedTaskInputQueue.run(queueKey, async () => {
    const hold = managedTaskInputQueueHoldForTests.get(queueKey);
    if (hold) {
      hold.notifyEntered();
      await hold.wait;
    }
    let latest = await ctx.taskInputs.get(item.id, item.workspaceId, item.taskPath);
    if (!latest) {
      return {
        input: item,
        continued: false,
        continueError: `TaskInput disappeared before managed inject: ${item.id}`
      };
    }
    if (latest.status !== "pending" && latest.status !== "failed") {
      return {
        input: latest,
        continued: latest.status === "delivered" || latest.status === "uncertain",
        continueError: `TaskInput already ${latest.status}; skip managed inject`
      };
    }
    const failRebind = async (prefix, err) => {
      const message2 = err instanceof Error ? err.message : String(err);
      try {
        latest = await ctx.taskInputs.markFailed(latest.id, `${prefix}: ${message2}`, "service");
      } catch {
      }
      return { input: latest, continued: false, continueError: `${prefix}: ${message2}` };
    };
    let sessionId;
    try {
      sessionId = await resolveManagedInjectSessionId(ctx, latest);
    } catch (err) {
      return failRebind("TaskInput exact Session guard failed", err);
    }
    if (!sessionId) return { input: latest, continued: false };
    if ((latest.sessionId?.trim() || "") !== sessionId) {
      try {
        latest = await ctx.taskInputs.rebindSession(
          latest.id,
          latest.workspaceId,
          latest.taskPath,
          sessionId
        );
      } catch (err) {
        return failRebind("TaskInput rebind to inject session failed", err);
      }
    }
    try {
      latest = await ctx.taskInputs.markProcessing(latest.id);
    } catch (err) {
      const message2 = err instanceof Error ? err.message : String(err);
      return {
        input: latest,
        continued: false,
        continueError: `TaskInput markProcessing failed: ${message2}`
      };
    }
    latest = await ctx.taskInputs.get(latest.id, latest.workspaceId, latest.taskPath) ?? latest;
    try {
      sessionId = await resolveManagedInjectSessionId(ctx, latest);
    } catch (err) {
      return failRebind("TaskInput exact Session guard failed after processing", err);
    }
    if (!sessionId) {
      return failRebind(
        "TaskInput exact Session guard failed after processing",
        new Error("Task no longer has a managed Session binding")
      );
    }
    if ((latest.sessionId?.trim() || "") && latest.sessionId.trim() !== sessionId) {
      try {
        const rows = await ctx.taskInputs.rebindOpenSessions(
          latest.workspaceId,
          latest.taskPath,
          sessionId,
          [latest.id]
        );
        latest = rows[0] ?? latest;
      } catch (err) {
        return failRebind("TaskInput inject session drift rebind failed", err);
      }
    }
    const forInject = { ...latest, sessionId };
    let continueResult;
    let finalInput = forInject;
    continueResult = await continueManagedAfterTaskInput(ctx, forInject);
    if (continueResult.continued) {
      try {
        finalInput = await ctx.taskInputs.markDelivered(forInject.id, "service", {
          sessionId
        });
        ctx.events.emit(
          "taskInput.delivered",
          forInject.workspaceId,
          {
            inputId: finalInput.id,
            taskPath: finalInput.taskPath,
            sessionId: finalInput.sessionId,
            kind: normalizeTaskInputKind(finalInput.kind),
            status: finalInput.status
          },
          "service"
        );
        trackManagedTaskInputBackground(
          requestManagedAutoSubmitRetryFromDraft(ctx, {
            workspaceId: forInject.workspaceId,
            taskPath: forInject.taskPath,
            sessionId
          })
        );
      } catch (err) {
        const message2 = err instanceof Error ? err.message : String(err);
        try {
          finalInput = await ctx.taskInputs.markUncertain(
            forInject.id,
            `managed inject ok but markDelivered failed: ${message2}`,
            "service",
            { sessionId }
          );
          ctx.events.emit(
            "taskInput.uncertain",
            forInject.workspaceId,
            {
              inputId: finalInput.id,
              taskPath: finalInput.taskPath,
              sessionId: finalInput.sessionId,
              kind: normalizeTaskInputKind(finalInput.kind),
              status: finalInput.status,
              lastError: finalInput.lastError
            },
            "service"
          );
        } catch {
        }
        return {
          input: finalInput,
          continued: true,
          continueError: `managed inject ok but markDelivered failed: ${message2}`
        };
      }
    } else {
      const failMsg = continueResult.error || "managed inject did not continue; external agent may poll taskInput";
      try {
        finalInput = await ctx.taskInputs.markFailed(forInject.id, failMsg, "service");
      } catch {
      }
    }
    return {
      input: finalInput,
      continued: continueResult.continued,
      continueError: continueResult.error
    };
  });
}
async function continueManagedAfterTaskInput(ctx, item) {
  if (!item.sessionId) return { continued: false };
  const prompt = formatTaskInputPrompt(item);
  try {
    try {
      await ctx.runtime.sendFollowUpPrompt(item.sessionId, prompt);
      return { continued: true };
    } catch (liveErr) {
      const liveMessage = liveErr instanceof Error ? liveErr.message : String(liveErr);
      try {
        const liveProbe = await ctx.runtime.probe(item.sessionId);
        if (liveProbe.isAlive && SessionRegistry.isNonTerminal(liveProbe.state)) {
          return {
            continued: false,
            error: liveMessage || "managed session live but does not support follow-up inject; external agent may poll taskInput.listPending / taskInput.get"
          };
        }
      } catch {
      }
      if (!/not alive|does not support live follow-up/i.test(liveMessage)) {
      }
    }
    const probe = await ctx.runtime.probe(item.sessionId);
    if (probe.isAlive && SessionRegistry.isNonTerminal(probe.state)) {
      return {
        continued: false,
        error: "managed session live but follow-up inject unavailable; external agent may poll taskInput.listPending / taskInput.get"
      };
    }
    if (!probe.canResume) {
      return {
        continued: false,
        error: "managed session not live and not resume-capable; external agent may poll taskInput.listPending / taskInput.get"
      };
    }
    const rec = await ctx.runtime.registry.read(item.sessionId);
    const cwd = rec?.runtimeWorkspace?.cwd;
    await ctx.runtime.resumeSession({
      sessionId: item.sessionId,
      bootstrapPrompt: prompt,
      ...cwd ? { runtimeWorkspace: { cwd } } : {}
    });
    return { continued: true };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    ctx.events.emit(
      "session.state",
      item.workspaceId,
      {
        sessionId: item.sessionId,
        taskPath: item.taskPath,
        runtimeEvent: "taskInput.continue.failed",
        error: message2,
        taskFailed: false
      },
      "service"
    );
    return { continued: false, error: message2 };
  }
}
async function assertManagedTurnIdleForPublicSubmit(ctx, task) {
  const sessionId = task.executionSessionId?.trim();
  if (!sessionId) return;
  const probe = await ctx.runtime.probe(sessionId);
  if (probe.isTurnActive !== true) return;
  throw new RpcError(
    RPC_LIFECYCLE,
    `task.submit refused: managed session ${sessionId} still has an in-flight turn (isTurnActive); task remains ${task.state} with no ready TaskResult until the turn settles`,
    {
      code: "TURN_BUSY",
      sessionId,
      taskPath: task.path,
      isTurnActive: true
    }
  );
}
async function assertNoBlockingTaskInputsForSubmit(ctx, workspaceId, task) {
  const pendingDecision = await ctx.decisionRequests.getPendingForTask(
    workspaceId,
    task.path
  );
  if (pendingDecision) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.submit refused: task has pending Decision Request ${pendingDecision.id}; respond or escalate it before TaskResult (task remains ${task.state}, no ready TaskResult)`,
      {
        code: "PENDING_DECISION_REQUEST",
        taskPath: task.path,
        ...task.id ? { taskId: task.id } : {},
        requestId: pendingDecision.id,
        target: pendingDecision.target
      }
    );
  }
  const blockers = await ctx.taskInputs.listBlockingForSubmit(
    workspaceId,
    task.path
  );
  if (blockers.length === 0) return;
  const ordered = [...blockers].sort(
    (a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id)
  );
  const first = ordered[0];
  throw new RpcError(
    RPC_LIFECYCLE,
    `task.submit refused: task has ${ordered.length} open TaskInput(s) (first ${first.id} status=${first.status}); consume or resolve them before TaskResult (task remains ${task.state}, no ready TaskResult)`,
    {
      code: "PENDING_TASK_INPUT",
      taskPath: task.path,
      ...task.id ? { taskId: task.id } : {},
      count: ordered.length,
      inputIds: ordered.map((i) => i.id),
      statuses: ordered.map((i) => i.status),
      firstInputId: first.id,
      firstStatus: first.status
    }
  );
}
async function requireRoleNameById(fs21, roleId) {
  const registry = await loadRolesRegistry(fs21);
  const role = registry.roles.find((item) => item.id === roleId);
  if (!role) throw new Error(`Role not found in registry: ${roleId}`);
  return role.name;
}
async function resolveTaskWorktreeForDirtyCheck(workspaceRoot, task, fs21) {
  const hasRecordedLane = Boolean(
    task.workspace || task.worktree || task.branch || task.targetBranch
  );
  if (!hasRecordedLane) return void 0;
  if (!await isGitWorkspace(workspaceRoot)) return void 0;
  const envelopeWt = task.worktree?.trim();
  if (envelopeWt) {
    return {
      worktree: nodePath6.resolve(envelopeWt),
      branch: task.branch
    };
  }
  const mountedRoot = nodePath6.resolve(workspaceRoot);
  const isTaskScoped = !task.assigneeRoleId;
  let targetBranch = task.targetBranch?.trim();
  if (isTaskScoped && taskIsDownstream(task)) {
    const parentRole = taskParentRoleId(task);
    if (parentRole) {
      const registry = await loadRolesRegistry(fs21);
      const role = registry.roles.find((item) => item.id === parentRole);
      if (!role) throw new Error(`Parent Role not found in registry: ${parentRole}`);
      targetBranch = (await ensureRoleWorkspace(mountedRoot, role.name)).branch;
    }
  }
  const lane = isTaskScoped ? await ensureTaskWorkspace(mountedRoot, task.id || task.path, {
    ...targetBranch ? { targetBranch } : {}
  }) : await ensureRoleWorkspace(mountedRoot, await requireRoleNameById(fs21, task.assigneeRoleId));
  return { worktree: lane.worktree, branch: lane.branch };
}
async function assertTaskWorktreeCleanForSubmit(workspaceRoot, task, fs21) {
  const lane = await resolveTaskWorktreeForDirtyCheck(workspaceRoot, task, fs21);
  if (!lane) return;
  const status = await inspectWorktreeDirtiness(lane.worktree);
  if (!status.dirty) return;
  throw new RpcError(
    RPC_LIFECYCLE,
    `task.submit refused: task worktree has uncommitted changes at ${lane.worktree} (${status.changeCount} change(s); tracked=${status.trackedDirty} untracked=${status.untrackedDirty}); commit or discard them, then retry result (task remains ${task.state}, no ready TaskResult)`,
    {
      code: "WORKTREE_DIRTY",
      taskPath: task.path,
      taskId: task.id,
      worktree: lane.worktree,
      ...lane.branch ? { branch: lane.branch } : {},
      trackedDirty: status.trackedDirty,
      untrackedDirty: status.untrackedDirty,
      changeCount: status.changeCount,
      dirtySample: status.sample
    }
  );
}
async function assertOrdinaryExecutorLaneHistoryForSubmit(workspaceRoot, task) {
  const branch = task.branch?.trim() || "";
  const hasExecutorLane = Boolean(
    branch || task.worktree?.trim() || task.workspace?.trim()
  );
  if (!hasExecutorLane) return;
  if (!await isGitWorkspace(workspaceRoot)) return;
  const base = task.baseCommit?.trim() || "";
  if (!base) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.submit refused: ordinary executor lane requires exact workspaceLane.baseCommit (recorded at Task worktree creation); roleBranchBase is not a TaskResult substitute (task remains ${task.state}, no ready TaskResult; lane/audit preserved)`,
      {
        code: "EXECUTOR_LANE_HISTORY",
        historyCode: "MISSING_BASE",
        taskPath: task.path,
        taskId: task.id,
        branch: branch || void 0
      }
    );
  }
  if (!branch) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.submit refused: ordinary executor lane missing branch for history tip (task remains ${task.state}, no ready TaskResult; lane/audit preserved)`,
      {
        code: "EXECUTOR_LANE_HISTORY",
        historyCode: "MISSING_TIP",
        taskPath: task.path,
        taskId: task.id,
        baseCommit: base
      }
    );
  }
  try {
    await assertOrdinaryExecutorLaneHistoryInGit({
      workspace: workspaceRoot,
      baseCommit: base,
      branch
    });
  } catch (err) {
    if (err instanceof ExecutorLaneHistoryError) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.submit refused: ordinary executor lane history gate failed (${err.code}): ${err.message} (task remains ${task.state}, no ready TaskResult; lane/audit preserved)`,
        {
          code: "EXECUTOR_LANE_HISTORY",
          historyCode: err.code,
          taskPath: task.path,
          taskId: task.id,
          baseCommit: base,
          branch,
          ...err.details ?? {}
        }
      );
    }
    throw err;
  }
}
async function resolveSubmitCommitsForExecutorLane(workspaceRoot, task, commits) {
  const refs = uniqueCommitRefs(commits);
  if (refs.length === 0) return [];
  const branch = task.branch?.trim() || "";
  const hasExecutorLane = Boolean(
    branch || task.worktree?.trim() || task.workspace?.trim()
  );
  if (!hasExecutorLane || !await isGitWorkspace(workspaceRoot)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "task.submit commits[] require an exact Git executor lane.",
      { code: "RESULT_COMMIT_LANE", laneCode: "MISSING_BRANCH", taskPath: task.path }
    );
  }
  const base = task.baseCommit?.trim() || "";
  if (!base) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.submit refused: commits[] require exact workspaceLane.baseCommit (task remains ${task.state}, no ready TaskResult; lane/audit preserved; Git untouched)`,
      {
        code: "RESULT_COMMIT_LANE",
        laneCode: "MISSING_BASE",
        taskPath: task.path,
        taskId: task.id,
        branch: branch || void 0
      }
    );
  }
  if (!branch) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.submit refused: commits[] require recorded executor branch (task remains ${task.state}, no ready TaskResult; lane/audit preserved; Git untouched)`,
      {
        code: "RESULT_COMMIT_LANE",
        laneCode: "MISSING_BRANCH",
        taskPath: task.path,
        taskId: task.id,
        baseCommit: base
      }
    );
  }
  try {
    return await resolveSubmitCommitsInExecutorLane({
      workspace: workspaceRoot,
      baseCommit: base,
      branch,
      commits: refs
    });
  } catch (err) {
    if (err instanceof SubmitCommitLaneError) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.submit refused: commits[] not in executor lane (${err.code}): ${err.message} (task remains ${task.state}, no ready TaskResult; Git untouched)`,
        {
          code: "RESULT_COMMIT_LANE",
          laneCode: err.code,
          taskPath: task.path,
          taskId: task.id,
          baseCommit: base,
          branch,
          ...err.details ?? {}
        }
      );
    }
    throw err;
  }
}
async function taskSubmitRpc(ctx, p, callContext) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const report = requireString(p, "report");
  const commits = optionalStringArray(p, "commits");
  const decision = optionalString2(p, "decision");
  const checks = Array.isArray(p.checks) ? p.checks : void 0;
  const artifactRefs = Array.isArray(p.artifactRefs) ? p.artifactRefs : void 0;
  const result = await runTaskLifecycle(workspaceId, taskPath, async () => {
    const taskForCaller = await loadTaskRecord(mount.env.fs, taskPath);
    await assertTaskSubmitCallerBinding(ctx, workspaceId, taskForCaller, callContext);
    let targetHead;
    let canonicalCommits = [];
    const prepared = await ctx.mutations.run(workspaceId, async () => {
      ctx.host.markSelfWrite(workspaceId);
      const taskForIntegrate = await loadTaskRecord(mount.env.fs, taskPath);
      await assertManagedTurnIdleForPublicSubmit(ctx, taskForIntegrate);
      await assertNoBlockingTaskInputsForSubmit(ctx, workspaceId, taskForIntegrate);
      await assertTaskWorktreeCleanForSubmit(mount.workspaceRoot, taskForIntegrate, mount.env.fs);
      await assertOrdinaryExecutorLaneHistoryForSubmit(mount.workspaceRoot, taskForIntegrate);
      canonicalCommits = await resolveSubmitCommitsForExecutorLane(
        mount.workspaceRoot,
        taskForIntegrate,
        commits
      );
      const recoverableResult = await loadRecoverableTaskSubmitResult(
        mount.env.fs,
        taskForIntegrate
      );
      targetHead = recoverableResult ? recoverableResult.targetHead ?? void 0 : canonicalCommits.length > 0 ? await snapshotIntegrationTargetHead(mount.workspaceRoot, taskForIntegrate, mount.env.fs) : void 0;
      if (targetHead && afterTargetHeadSnapshotForTests) {
        await afterTargetHeadSnapshotForTests(mount.workspaceRoot);
      }
      return prepareTaskSubmit(mount.env, taskPath, {
        report,
        commits: canonicalCommits,
        ...targetHead ? { targetHead } : {},
        checks,
        artifactRefs,
        decision
      });
    });
    if (prepared.kind === "done") return prepared.result;
    canonicalCommits = [...prepared.commits];
    targetHead = prepared.targetHead;
    if (canonicalCommits.length > 0) {
      const taskForIntegrate = await loadTaskRecord(mount.env.fs, taskPath);
      await makeCommitIntegrator(ctx, mount.workspaceRoot, taskForIntegrate, {
        expectedTargetHead: targetHead,
        action: "task.submit",
        taskPath
      })(canonicalCommits);
    }
    await beforeTaskSubmitFinalizeForTests?.({
      workspaceId,
      taskPath,
      resultId: prepared.resultId
    });
    return ctx.mutations.run(workspaceId, async () => {
      ctx.host.markSelfWrite(workspaceId);
      return finalizeTaskSubmitAuto(
        mount.env,
        taskPath,
        {
          report,
          commits: canonicalCommits,
          ...targetHead ? { targetHead } : {},
          checks,
          artifactRefs,
          decision
        },
        prepared
      );
    });
  });
  emitTaskState(ctx, workspaceId, result.task, "task.submit");
  ctx.events.emit(
    "taskResult.updated",
    workspaceId,
    {
      id: result.result.id,
      taskId: result.result.taskId,
      status: result.result.status,
      reason: "task.submit"
    },
    "self"
  );
  return {
    workspaceId,
    taskPath,
    task: projectTask(result.task),
    result: projectTaskResult(result.result),
    autoIntegrated: result.autoIntegrated,
    state: result.task.state
  };
}
async function assertTaskSubmitCallerBinding(ctx, workspaceId, task, callContext) {
  const callerSessionId = callContext.callerSessionId?.trim();
  if (!callerSessionId) return;
  const session = await ctx.runtime.registry.read(callerSessionId);
  if (!task.id || !session || task.executionSessionId !== callerSessionId || session.workspace !== workspaceId || session.currentTaskId !== task.id) {
    throw new RpcError(-32001, "task.submit caller is not the exact Session bound to this Task", {
      code: "TASK_SUBMIT_SESSION_MISMATCH",
      taskId: task.id,
      taskSessionId: task.executionSessionId,
      callerSessionId
    });
  }
}
async function loadRecoverableTaskSubmitResult(fs21, task) {
  const resultId = task.currentResultId;
  if (!resultId) return void 0;
  const resultPath = taskResultPathForTask(task.path, resultId);
  if (!await fs21.exists(resultPath)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Task currentResultId points to a missing exact TaskResult: ${resultId}`,
      { code: "RESULT_CHANGED", resultId, taskId: task.id }
    );
  }
  const result = await loadTaskResult(fs21, resultPath);
  if (result.path !== resultPath || result.id !== resultId || result.taskId !== task.id) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Task currentResultId does not identify its exact TaskResult: ${resultId}`,
      { code: "RESULT_CHANGED", resultId, taskId: task.id }
    );
  }
  return result.status === "ready" || result.status === "accepted" ? result : void 0;
}
async function resolveReviewCallerActor(ctx, workspaceId, requestedActor, action, callContext) {
  const callerSessionId = callContext.callerSessionId?.trim();
  let derivedActor = "user";
  if (callerSessionId) {
    const session = await ctx.runtime.registry.read(callerSessionId);
    if (!session || session.state !== "external" || session.workspace !== workspaceId || !session.roleId) {
      throw new RpcError(
        -32001,
        `task.${action} requires an exact authenticated external Role Session reviewer`,
        {
          code: "REVIEW_CALLER_FORBIDDEN",
          action,
          workspaceId,
          callerSessionId
        }
      );
    }
    derivedActor = session.roleId;
  } else if (callContext.callerExternalKey?.trim()) {
    throw new RpcError(
      -32001,
      `task.${action} external host identity requires a valid Session capability`,
      {
        code: "REVIEW_CALLER_SESSION_REQUIRED",
        action,
        workspaceId
      }
    );
  }
  if (requestedActor.trim() !== derivedActor) {
    throw new RpcError(
      -32001,
      `task.${action} actor does not match authenticated caller authority`,
      {
        code: "REVIEW_CALLER_MISMATCH",
        action,
        workspaceId,
        requestedActor: requestedActor.trim(),
        derivedActor,
        ...callerSessionId ? { callerSessionId } : {}
      }
    );
  }
  return derivedActor;
}
function assertAcceptedResultForOutputBinding(review, actor) {
  const requester = review.task.requester;
  const expectedKind = actor === "user" ? "user" : "role";
  if (!requester || requester.kind !== expectedKind || requester.id !== actor) {
    throw new RpcError(-32001, "task.bindOutput requires the exact persisted requester", {
      code: "OUTPUT_BIND_CALLER_FORBIDDEN",
      resultId: review.result.id,
      actor,
      requester
    });
  }
  if (review.task.state !== "accepted" || review.task.currentResultId !== review.result.id || review.result.status !== "accepted") {
    throw new RpcError(RPC_LIFECYCLE, "task.bindOutput requires an accepted exact TaskResult", {
      code: "RESULT_NOT_ACCEPTED",
      resultId: review.result.id,
      taskId: review.task.id,
      taskState: review.task.state,
      resultStatus: review.result.status
    });
  }
}
async function requireUniqueTaskForReviewTaskResult(ctx, workspaceId, resultId) {
  const mount = ctx.host.require(workspaceId);
  const tasks = await loadTaskRecords(mount.env.fs);
  const currentMatches = tasks.filter(
    (task2) => typeof task2.id === "string" && task2.currentResultId === resultId
  );
  if (currentMatches.length !== 1) {
    throw reviewTaskResultTaskLookupError(resultId, currentMatches.length, 0);
  }
  const task = currentMatches[0];
  const duplicateTaskIds = tasks.filter((candidate) => candidate.id === task.id);
  if (duplicateTaskIds.length !== 1) {
    throw reviewTaskResultTaskLookupError(resultId, duplicateTaskIds.length, 0);
  }
  const resultPath = taskResultPathForTask(task.path, resultId);
  if (!await mount.env.fs.exists(resultPath)) {
    throw reviewTaskResultTaskLookupError(resultId, 1, 0);
  }
  const result = await loadTaskResult(mount.env.fs, resultPath);
  if (result.path !== resultPath || result.id !== resultId || result.taskId !== task.id) {
    throw reviewTaskResultTaskLookupError(resultId, 1, 0);
  }
  return { task, result };
}
function reviewTaskResultTaskLookupError(resultId, taskMatches, resultMatches = 1) {
  return new RpcError(
    taskMatches === 0 && resultMatches === 0 ? -32004 : RPC_LIFECYCLE,
    `Review TaskResult must identify exactly one durable TaskResult and canonical Task: ${resultId}`,
    {
      code: "REVIEW_RESULT_TASK_NOT_UNIQUE",
      resultId,
      resultMatches,
      taskMatches
    }
  );
}
function assertReviewTaskResultIdentityUnchanged(initial, current) {
  const initialDigest = taskResultReviewSemanticsDigest(initial.result);
  const currentDigest = taskResultReviewSemanticsDigest(current.result);
  if (current.task.path !== initial.task.path || current.task.id !== initial.task.id || current.result.path !== initial.result.path || currentDigest !== initialDigest) {
    throw new RpcError(RPC_LIFECYCLE, "Review TaskResult changed during mutation", {
      code: "RESULT_CHANGED",
      resultId: initial.result.id,
      expectedDigest: initialDigest,
      actualDigest: currentDigest
    });
  }
}
function assertRecoveredReviewIdentityUnchanged(current, recovered) {
  const taskMatches = recovered.task.state === "accepted" && current.task.path === recovered.task.path && current.task.id === recovered.task.id && current.task.state === recovered.task.state && current.task.currentResultId === recovered.task.currentResultId && current.task.updatedAt === recovered.task.updatedAt && current.task.wait == null && recovered.task.wait == null;
  const resultMatches = recovered.result.status === "accepted" && current.result.path === recovered.result.path && current.result.id === recovered.result.id && current.result.taskId === recovered.result.taskId && taskResultReviewSemanticsDigest(current.result) === taskResultReviewSemanticsDigest(recovered.result);
  if (!taskMatches || !resultMatches) {
    throw new RpcError(RPC_LIFECYCLE, "Recovered review authority changed before response", {
      code: "RESULT_CHANGED",
      resultId: recovered.result.id
    });
  }
}
async function taskAcceptRpc(ctx, p, callContext) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "resultId", "actor"]),
    "task.accept"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const resultId = requireString(p, "resultId");
  if (!isTaskResultId(resultId)) {
    throw new RpcError(-32602, `Invalid TaskResult id: ${resultId}`);
  }
  const initialReview = await requireUniqueTaskForReviewTaskResult(ctx, workspaceId, resultId);
  const taskPath = initialReview.task.path;
  const actor = requireString(p, "actor");
  const result = await runTaskLifecycle(workspaceId, taskPath, async () => {
    const reviewerActor = await resolveReviewCallerActor(
      ctx,
      workspaceId,
      actor,
      "accept",
      callContext
    );
    const acceptOptions = { actor: reviewerActor, resultId };
    let prepared;
    let expectedTargetHead;
    try {
      prepared = await ctx.mutations.run(workspaceId, async () => {
        ctx.host.markSelfWrite(workspaceId);
        const exactReview = await requireUniqueTaskForReviewTaskResult(
          ctx,
          workspaceId,
          resultId
        );
        assertReviewTaskResultIdentityUnchanged(initialReview, exactReview);
        const taskForIntegrate = await loadTaskRecord(mount.env.fs, taskPath);
        expectedTargetHead = await loadReadyTaskResultTargetHead(
          mount.env.fs,
          taskForIntegrate
        );
        return prepareTaskAccept(mount.env, taskPath, acceptOptions);
      });
    } catch (err) {
      if (err instanceof OutputProvenanceError) throw outputProvenanceErrorToRpc(err);
      throw err;
    }
    if (prepared.commits.length > 0) {
      const taskForIntegrate = await loadTaskRecord(mount.env.fs, taskPath);
      await makeCommitIntegrator(ctx, mount.workspaceRoot, taskForIntegrate, {
        expectedTargetHead,
        action: "task.accept",
        taskPath
      })(prepared.commits);
      await beforeTaskAcceptFinalizeForTests?.({ workspaceId, taskPath, resultId });
    }
    try {
      return await ctx.mutations.run(workspaceId, async () => {
        ctx.host.markSelfWrite(workspaceId);
        const exactReview = await requireUniqueTaskForReviewTaskResult(
          ctx,
          workspaceId,
          resultId
        );
        if (prepared.recovered) {
          assertRecoveredReviewIdentityUnchanged(exactReview, prepared.recovered);
        } else {
          assertReviewTaskResultIdentityUnchanged(initialReview, exactReview);
        }
        return finalizeTaskAccept(mount.env, taskPath, acceptOptions, prepared);
      });
    } catch (err) {
      if (err instanceof OutputProvenanceError) throw outputProvenanceErrorToRpc(err);
      throw err;
    }
  });
  emitTaskState(ctx, workspaceId, result.task, "task.accept");
  ctx.events.emit(
    "taskResult.updated",
    workspaceId,
    {
      id: result.result.id,
      taskId: result.result.taskId,
      status: result.result.status,
      reason: "task.accept"
    },
    "self"
  );
  return {
    workspaceId,
    taskPath,
    task: projectTask(result.task),
    result: projectTaskResult(result.result),
    state: result.task.state
  };
}
async function outputProvenanceRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = requireString(p, "nodeId");
  if (!isNodeId(nodeId)) throw new RpcError(-32602, `Invalid Node id: ${nodeId}`);
  try {
    const projected = await resolveOutputProvenance(mount.env.fs, { nodeId });
    return projectOutputProvenanceWire(workspaceId, projected);
  } catch (err) {
    if (err instanceof OutputProvenanceError) throw outputProvenanceErrorToRpc(err);
    throw err;
  }
}
async function taskBindOutputRpc(ctx, p, callContext) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "resultId", "outputNodeIds", "actor"]),
    "task.bindOutput"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const resultId = requireString(p, "resultId");
  if (!isTaskResultId(resultId)) {
    throw new RpcError(-32602, `Invalid TaskResult id: ${resultId}`);
  }
  const outputNodeIds = optionalStringArray(p, "outputNodeIds") ?? [];
  if (outputNodeIds.length === 0) {
    throw new RpcError(-32602, "task.bindOutput requires at least one outputNodeId");
  }
  const actor = requireString(p, "actor");
  const reviewerActor = await resolveReviewCallerActor(
    ctx,
    workspaceId,
    actor,
    "bindOutput",
    callContext
  );
  const initialReview = await requireUniqueTaskForReviewTaskResult(
    ctx,
    workspaceId,
    resultId
  );
  assertAcceptedResultForOutputBinding(initialReview, reviewerActor);
  let bound;
  try {
    bound = await ctx.mutations.run(workspaceId, async () => {
      const exactReview = await requireUniqueTaskForReviewTaskResult(
        ctx,
        workspaceId,
        resultId
      );
      assertReviewTaskResultIdentityUnchanged(initialReview, exactReview);
      assertAcceptedResultForOutputBinding(exactReview, reviewerActor);
      ctx.host.markSelfWrite(workspaceId);
      return bindOutputsToTaskResult(
        mount.env.fs,
        outputNodeIds,
        resultId
      );
    });
  } catch (err) {
    if (err instanceof OutputProvenanceError) throw outputProvenanceErrorToRpc(err);
    throw err;
  }
  if (bound.changedIds.length > 0) {
    const tent = await loadTent(mount.env.fs);
    for (const nodeId of bound.changedIds) {
      const node2 = tent.byId.get(nodeId);
      ctx.events.emit(
        "node.changed",
        workspaceId,
        {
          nodeId,
          ...node2 ? { path: node2.path } : {},
          resultId,
          reason: "task.bindOutput"
        },
        "self"
      );
    }
  }
  return {
    workspaceId,
    taskPath: initialReview.task.path,
    resultId,
    outputNodeIds: bound.boundIds,
    changedNodeIds: bound.changedIds
  };
}
function projectOutputProvenanceWire(workspaceId, core) {
  return {
    workspaceId,
    outputId: core.outputId,
    path: core.path,
    bound: core.bound,
    resultId: core.resultId,
    result: core.result ? { ...core.result, artifactRefs: core.result.artifactRefs.map((ref) => ({ ...ref })) } : null,
    task: core.task,
    incomplete: core.incomplete
  };
}
function outputProvenanceErrorToRpc(err) {
  switch (err.code) {
    case "OUTPUT_NOT_FOUND":
      return new RpcError(-32004, err.message, err.details);
    case "INVALID_SELECTOR":
      return new RpcError(-32602, err.message, err.details);
    case "OUTPUT_INVALID":
    case "OUTPUT_ARCHIVED":
    case "OUTPUT_NOT_OUTPUT_TYPE":
    case "OUTPUT_ALREADY_BOUND":
    case "INVALID_RESULT_ID":
    case "BIND_ROLLBACK_FAILED":
      return new RpcError(-32010, err.message, { code: err.code, ...err.details });
    default:
      return new RpcError(-32010, err.message, { code: err.code, ...err.details });
  }
}
async function taskRejectRpc(ctx, p, callContext) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "resultId", "actor", "note", "resume"]),
    "task.reject"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const resultId = requireString(p, "resultId");
  if (!isTaskResultId(resultId)) {
    throw new RpcError(-32602, `Invalid TaskResult id: ${resultId}`);
  }
  const initialReview = await requireUniqueTaskForReviewTaskResult(ctx, workspaceId, resultId);
  const taskPath = initialReview.task.path;
  const actor = requireString(p, "actor");
  const noteForTaskResult = optionalString2(p, "note");
  const noteExact = optionalStringExact(p, "note");
  const resume = p.resume !== false;
  const result = await runTaskLifecycle(workspaceId, taskPath, async () => {
    const reviewerActor = await resolveReviewCallerActor(
      ctx,
      workspaceId,
      actor,
      "reject",
      callContext
    );
    return ctx.mutations.run(workspaceId, async () => {
      ctx.host.markSelfWrite(workspaceId);
      const exactReview = await requireUniqueTaskForReviewTaskResult(
        ctx,
        workspaceId,
        resultId
      );
      assertReviewTaskResultIdentityUnchanged(initialReview, exactReview);
      const rejected = await taskReject(mount.env, taskPath, {
        actor: reviewerActor,
        resultId,
        note: noteForTaskResult,
        resume
      });
      const reviewInput2 = resume ? await createRejectResumeReviewFeedback(ctx, {
        workspaceId,
        taskPath,
        task: rejected.task,
        resultId: rejected.result.id,
        note: noteExact !== void 0 ? noteExact : rejected.result.review?.note || DEFAULT_TASK_REJECT_NOTE
      }) : void 0;
      let externalRestoreError;
      if (resume && rejected.task.assigneeRoleId && rejected.task.executionSessionId) {
        try {
          await restoreExactExternalRoleTask(ctx, { workspaceId, taskPath });
        } catch (error) {
          externalRestoreError = error;
        }
      }
      emitTaskState(ctx, workspaceId, rejected.task, "task.reject");
      ctx.events.emit(
        "taskResult.updated",
        workspaceId,
        {
          id: rejected.result.id,
          taskId: rejected.result.taskId,
          status: rejected.result.status,
          reason: "task.reject"
        },
        "self"
      );
      return { ...rejected, reviewInput: reviewInput2, externalRestoreError };
    });
  });
  if (!resume) {
    return {
      workspaceId,
      taskPath,
      task: projectTask(result.task),
      result: projectTaskResult(result.result),
      state: result.task.state
    };
  }
  const reviewInput = result.reviewInput;
  const boundSessionId = result.task.executionSessionId?.trim() || "";
  if (!boundSessionId) {
    return {
      workspaceId,
      taskPath,
      task: projectTask(result.task),
      result: projectTaskResult(result.result),
      state: result.task.state,
      input: projectTaskInput(reviewInput),
      /** Durable review-feedback accepted; no managed inject scheduled. */
      accepted: true,
      enqueued: false,
      /** Always false on accept — external agents poll taskInput.* */
      continued: false
    };
  }
  if (result.task.assigneeRoleId) {
    if (result.externalRestoreError === void 0) {
      return {
        workspaceId,
        taskPath,
        task: projectTask(result.task),
        result: projectTaskResult(result.result),
        state: result.task.state,
        input: projectTaskInput(reviewInput),
        /** Durable review-feedback accepted for external poll/get/ack. */
        accepted: true,
        enqueued: false,
        continued: false
      };
    }
    const message2 = result.externalRestoreError instanceof Error ? result.externalRestoreError.message : String(result.externalRestoreError);
    await parkTaskAfterRejectResumeFailure(ctx, {
      workspaceId,
      taskPath,
      sessionId: boundSessionId,
      message: message2,
      summaryPrefix: EXTERNAL_ROLE_REJECT_RESUME_FAILED_WAIT_SUMMARY
    });
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.reject resume failed to restore external Role Session: ${message2}`,
      { taskPath, sessionId: boundSessionId, inputId: reviewInput.id }
    );
  }
  try {
    const restored = await restoreManagedSessionAfterRejectResume(ctx, {
      workspaceId,
      taskPath
    });
    const restoredSessionId = restored.session.sessionId;
    if (restoredSessionId !== boundSessionId) {
      throw new Error(
        `reject-resume must preserve the exact Session id; expected ${boundSessionId}, got ${restoredSessionId}`
      );
    }
    enqueueManagedTaskInputBackground(ctx, reviewInput);
    return {
      workspaceId,
      taskPath,
      task: projectTask(restored.task),
      result: projectTaskResult(result.result),
      state: restored.task.state,
      session: restored.session,
      input: projectTaskInput(reviewInput),
      /** Durable row + restore accepted; does not mean provider turn finished. */
      accepted: true,
      /** Managed session restored and background inject scheduled. */
      enqueued: true,
      /** Always false on accept — poll taskInput.get / events for delivered|failed. */
      continued: false
    };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    await parkTaskAfterRejectResumeFailure(ctx, {
      workspaceId,
      taskPath,
      sessionId: boundSessionId,
      message: message2
    });
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.reject resume failed to restore managed session: ${message2}`,
      { taskPath, sessionId: boundSessionId, inputId: reviewInput.id }
    );
  }
}
async function createRejectResumeReviewFeedback(ctx, input) {
  const now = (/* @__PURE__ */ new Date()).toISOString();
  const text3 = typeof input.note === "string" ? input.note : "";
  const id = taskRejectResumeFeedbackId(input.resultId);
  const existing = await ctx.taskInputs.get(id, input.workspaceId, input.taskPath);
  if (existing) {
    if (!rejectResumeFeedbackMatches(existing, {
      ...input,
      note: text3,
      exactText: true
    })) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Reject-resume continuation ${id} conflicts with its exact TaskResult request.`,
        {
          code: "TASK_REJECT_RESUME_CONTINUATION_CHANGED",
          taskPath: input.taskPath,
          resultId: input.resultId,
          inputId: id
        }
      );
    }
    return existing;
  }
  const record = await ctx.taskInputs.add({
    id,
    workspaceId: input.workspaceId,
    taskPath: input.taskPath,
    taskId: input.task.id,
    sessionId: input.task.executionSessionId || void 0,
    role: taskParentRoleId(input.task),
    kind: "review-feedback",
    text: text3,
    status: "pending",
    createdAt: now,
    updatedAt: now
  });
  ctx.events.emit(
    "taskInput.pending",
    input.workspaceId,
    {
      inputId: record.id,
      taskPath: record.taskPath,
      taskId: record.taskId,
      sessionId: record.sessionId,
      role: record.role,
      kind: "review-feedback",
      text: record.text,
      createdAt: record.createdAt
    },
    "self"
  );
  return record;
}
async function taskInterruptRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const result = await runTaskLifecycle(workspaceId, taskPath, () => ctx.mutations.run(workspaceId, async () => {
    const before = await loadTaskRecord(mount.env.fs, taskPath).catch(() => null);
    const sessionId = before?.executionSessionId;
    ctx.host.markSelfWrite(workspaceId);
    await promoteManagedDraftBeforeTerminal(ctx, workspaceId, taskPath, before);
    const task = await taskInterrupt(mount.env, taskPath);
    emitTaskState(ctx, workspaceId, task, "task.interrupt");
    await clearManagedDraftBestEffort(ctx, workspaceId, taskPath);
    await removePendingDecisionRequestForTerminal(
      ctx,
      workspaceId,
      taskPath,
      "task.interrupt"
    );
    await cancelTaskInputsForTask(ctx, workspaceId, taskPath, "task.interrupt");
    if (sessionId) {
      try {
        await ctx.toolApprovals.cancelSession(sessionId, "denied");
      } catch {
      }
      try {
        await cancelTaskInputsForSession(
          ctx,
          workspaceId,
          sessionId,
          "task.interrupt"
        );
      } catch {
      }
      try {
        await ctx.runtime.stopSession(sessionId, "interrupt");
      } catch {
      }
    }
    return {
      workspaceId,
      taskPath,
      task: projectTask(task),
      state: task.state
    };
  }));
  return {
    workspaceId: result.workspaceId,
    taskPath: result.taskPath,
    task: result.task,
    state: result.state
  };
}
async function taskCancelRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const result = await runTaskLifecycle(workspaceId, taskPath, () => ctx.mutations.run(workspaceId, async () => {
    const before = await loadTaskRecord(mount.env.fs, taskPath).catch(() => null);
    ctx.host.markSelfWrite(workspaceId);
    await taskCancel(mount.env, taskPath);
    ctx.events.emit(
      "task.state",
      workspaceId,
      { path: taskPath, state: "interrupted", reason: "task.cancel" },
      "self"
    );
    await clearManagedDraftBestEffort(ctx, workspaceId, taskPath);
    await removePendingDecisionRequestForTerminal(
      ctx,
      workspaceId,
      taskPath,
      "task.cancel"
    );
    return {
      workspaceId,
      taskPath,
      state: "interrupted",
      cancelled: true
    };
  }));
  return {
    workspaceId: result.workspaceId,
    taskPath: result.taskPath,
    state: result.state,
    cancelled: result.cancelled
  };
}
async function promoteManagedDraftBeforeTerminal(ctx, workspaceId, taskPath, task) {
  if (!task || task.state !== "running" && task.state !== "waiting") return task;
  const draft = await ctx.managedTaskResultReportDrafts.get(workspaceId, taskPath);
  if (!draft) return task;
  if (!task.id || draft.taskId !== task.id || !task.executionSessionId || draft.sessionId !== task.executionSessionId) {
    return task;
  }
  const draftOutcome = parseTaskOutcomeReport(draft.assistantText);
  const visibleReport = draftOutcome?.outcome === "blocked" ? draftOutcome.report || draft.assistantText : draft.assistantText;
  const exactVisible = task.statusDetail?.report === visibleReport && (!task.statusDetail.executionSessionId || task.statusDetail.executionSessionId === draft.sessionId) && (!draftOutcome || task.statusDetail.kind === draftOutcome.outcome);
  if (exactVisible) return task;
  const mount = ctx.host.require(workspaceId);
  const reportFits = Buffer.byteLength(draft.assistantText, "utf8") <= TASK_STATUS_DETAIL_REPORT_MAX_BYTES;
  return taskRecordFailedReturn(mount.env, taskPath, {
    ...reportFits ? { report: draft.assistantText } : {},
    error: reportFits ? "Task ended before its managed return could be published." : "Managed return draft exceeded the Task return bound before termination.",
    code: reportFits ? "TASK_TERMINATED_WITH_DRAFT" : "TASK_TERMINATED_DRAFT_OVERSIZE",
    executionSessionId: draft.sessionId
  });
}
async function clearManagedDraftBestEffort(ctx, workspaceId, taskPath) {
  try {
    await ctx.managedTaskResultReportDrafts.clear(workspaceId, taskPath);
  } catch {
  }
}
async function taskStartSessionRpc(ctx, p) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath", "callerKind", "bootstrapPrompt"]),
    "task.startSession"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const taskPath = requireString(p, "taskPath");
  const callerKind = parseCallerKind(optionalString2(p, "callerKind") ?? "user");
  const route = await readExactManagedSessionRoute(ctx, workspaceId, taskPath);
  return runManagedSessionFlight(workspaceId, taskPath, route, "startSession", async () => {
    const prepared = await prepareAuthorizedTaskStartSession(
      ctx,
      p,
      workspaceId,
      taskPath,
      callerKind
    );
    assertManagedSessionRouteUnchanged("task.startSession", taskPath, route, prepared);
    if (prepared.kind === "reuse") {
      const result = await runTaskLifecycle(workspaceId, taskPath, async () => {
        const mount = ctx.host.require(workspaceId);
        const current = await reconcileServiceTaskLifecycleSerialized(
          ctx,
          workspaceId,
          taskPath
        );
        const sessionId = prepared.result.session.sessionId;
        if (current.state !== "running" && current.state !== "waiting" || current.executionSessionId !== sessionId) {
          throw new RpcError(
            RPC_LIFECYCLE,
            "task.startSession: Task changed before same-Task Session reuse could return",
            {
              code: "TASK_SESSION_BIND_CAS_FAILED",
              taskPath,
              sessionId,
              state: current.state,
              currentSessionId: current.executionSessionId
            }
          );
        }
        const session = await ctx.runtime.registry.read(sessionId);
        if (!session) {
          throw new RpcError(
            RPC_LIFECYCLE,
            `task.startSession: bound Session ${sessionId} disappeared`,
            { code: "BOUND_SESSION_MISSING", taskPath, sessionId }
          );
        }
        return projectStartSessionResult(workspaceId, taskPath, current, session, {
          cwd: current.worktree || mount.workspaceRoot
        });
      });
      await scheduleRetryableTaskInputsAfterSessionBind(ctx, {
        workspaceId,
        taskPath
      });
      return result;
    }
    return launchAndBindTaskStartSession(ctx, prepared);
  });
}
function captureTaskSessionBindSnapshot(task) {
  return {
    taskId: task.id,
    state: task.state,
    sessionId: task.executionSessionId?.trim() || "",
    updatedAt: task.updatedAt,
    roleId: task.assigneeRoleId,
    nodeContextJson: JSON.stringify({
      nodeIds: task.nodeIds,
      nodeSnapshots: task.nodeSnapshots
    }),
    workspace: task.workspace,
    worktree: task.worktree,
    branch: task.branch,
    targetBranch: task.targetBranch,
    baseCommit: task.baseCommit,
    acceptMode: task.acceptMode,
    requester: task.requester
  };
}
function assertTaskSessionBindSnapshot(operation, taskPath, current, expected) {
  const actual = captureTaskSessionBindSnapshot(current);
  const unchanged = actual.taskId === expected.taskId && actual.state === expected.state && actual.sessionId === expected.sessionId && actual.updatedAt === expected.updatedAt && actual.roleId === expected.roleId && actual.workspace === expected.workspace && actual.worktree === expected.worktree && actual.branch === expected.branch && actual.targetBranch === expected.targetBranch && actual.baseCommit === expected.baseCommit && actual.acceptMode === expected.acceptMode && JSON.stringify(actual.requester) === JSON.stringify(expected.requester) && actual.nodeContextJson === expected.nodeContextJson;
  if (unchanged && current.state === "running") return;
  throw new RpcError(
    RPC_LIFECYCLE,
    `${operation}: Task changed while the managed Session was starting; refusing late bind`,
    {
      code: "TASK_SESSION_BIND_CAS_FAILED",
      taskPath,
      expected,
      actual
    }
  );
}
function assertTaskSessionPostStartOwnership(operation, taskPath, current, expected) {
  const actual = captureTaskSessionBindSnapshot(current);
  const immutableIdentityUnchanged = actual.taskId === expected.taskId && actual.sessionId === expected.sessionId && actual.roleId === expected.roleId && actual.workspace === expected.workspace && actual.worktree === expected.worktree && actual.branch === expected.branch && actual.targetBranch === expected.targetBranch && actual.baseCommit === expected.baseCommit && actual.acceptMode === expected.acceptMode && JSON.stringify(actual.requester) === JSON.stringify(expected.requester) && actual.nodeContextJson === expected.nodeContextJson;
  const validSameSessionProgress = current.state === "running" || current.state === "waiting" || current.state === "submitted";
  if (immutableIdentityUnchanged && validSameSessionProgress) return;
  throw new RpcError(
    RPC_LIFECYCLE,
    `${operation}: Task no longer owns the managed Session after provider start`,
    {
      code: "TASK_SESSION_BIND_CAS_FAILED",
      taskPath,
      expected,
      actual
    }
  );
}
function isTaskSessionBindCasError(err) {
  return err instanceof RpcError && err.data?.code === "TASK_SESSION_BIND_CAS_FAILED";
}
async function stopUnboundManagedSession(ctx, sessionId, operation, detail) {
  let stopped = true;
  try {
    await ctx.runtime.stopSession(sessionId, "interrupt");
  } catch {
    stopped = false;
  }
  try {
    await ctx.runtime.registry.update(sessionId, {
      lastError: `${operation} unbound Session cleanup: ${detail}`,
      ...stopped ? { state: "stopped", stopReason: "interrupt" } : {}
    });
  } catch {
  }
  return stopped;
}
async function readExactManagedSessionRoute(ctx, workspaceId, taskPath) {
  const mount = ctx.host.require(workspaceId);
  const task = await loadTaskRecord(mount.env.fs, taskPath);
  const sessionId = task.executionSessionId?.trim() || "";
  if (!sessionId) {
    throw new RpcError(
      -32602,
      "task.startSession/task.replaceSession requires an exact bound Session"
    );
  }
  const session = await ctx.runtime.registry.read(sessionId);
  if (!session?.connectionId) {
    throw new RpcError(RPC_LIFECYCLE, `Bound managed Session not found: ${sessionId}`, {
      code: "BOUND_SESSION_MISSING",
      taskPath,
      sessionId
    });
  }
  const taskId = task.id;
  if (!taskId || !isTaskId(taskId)) {
    throw new RpcError(RPC_LIFECYCLE, "Task has no canonical id for managed Session routing", {
      code: "TASK_ID_INVALID",
      taskPath
    });
  }
  if (session.workspace !== workspaceId || session.currentTaskId !== taskId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound Session ${sessionId} does not match the exact Task/workspace`,
      { code: "BOUND_SESSION_IDENTITY_MISMATCH", taskPath, sessionId }
    );
  }
  return { taskId, sessionId, connectionId: session.connectionId };
}
function assertManagedSessionRouteUnchanged(operation, taskPath, route, prepared) {
  const preparedSessionId = prepared.kind === "reuse" ? prepared.result.session.sessionId : prepared.task.executionSessionId?.trim() || "";
  const preparedTaskId = prepared.kind === "reuse" ? prepared.result.task.id : prepared.task.id;
  if (prepared.connectionId === route.connectionId && preparedSessionId === route.sessionId && preparedTaskId === route.taskId) {
    return;
  }
  throw new RpcError(
    RPC_LIFECYCLE,
    `${operation}: exact Task/Session/Connection route changed before managed flight`,
    {
      code: "TASK_SESSION_ROUTE_CHANGED",
      taskPath,
      expected: route,
      actual: {
        connectionId: prepared.connectionId,
        sessionId: preparedSessionId,
        taskId: preparedTaskId
      }
    }
  );
}
async function prepareAuthorizedTaskStartSession(ctx, p, workspaceId, taskPath, callerKind, opts) {
  const mount = ctx.host.require(workspaceId);
  const bootstrapPrompt = optionalString2(p, "bootstrapPrompt");
  let task = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
  const boundSessionId = task.executionSessionId?.trim() || "";
  if (!boundSessionId) {
    throw new RpcError(
      -32602,
      "task.startSession/task.replaceSession requires an exact bound Session"
    );
  }
  const boundSession = await ctx.runtime.registry.read(boundSessionId);
  if (!boundSession || !boundSession.connectionId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound managed Session not found: ${boundSessionId}`,
      { code: "BOUND_SESSION_MISSING", taskPath, sessionId: boundSessionId }
    );
  }
  const exactTaskId = task.id;
  if (boundSession.workspace !== workspaceId || boundSession.currentTaskId !== exactTaskId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound Session ${boundSessionId} does not match the exact Task/workspace`,
      { code: "BOUND_SESSION_IDENTITY_MISMATCH", taskPath, sessionId: boundSessionId }
    );
  }
  const connectionId = boundSession.connectionId;
  if (opts?.skipReuseAndLaunchPrep) {
    return {
      kind: "launch",
      workspaceId,
      taskPath,
      connectionId,
      task,
      bootstrapPrompt
    };
  }
  if (task.state === "queued" && callerKind === "user") {
    await taskClaimRpc(ctx, { workspaceId, taskPath });
    task = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
  }
  if (task.state !== "running" && task.state !== "waiting") {
    throw new RpcError(
      RPC_LIFECYCLE,
      `task.startSession requires running or waiting; got ${task.state}`
    );
  }
  if (task.state === "waiting") {
    if (!isSessionUnavailableParkedWait(task)) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "task.startSession allows waiting only with durable waitCode=session_unavailable; resolve the existing wait first",
        {
          code: "INVALID_TASK_WAIT",
          taskPath,
          waitReason: task.wait?.reason,
          waitCode: task.wait?.code
        }
      );
    }
    task = await runTaskLifecycle(workspaceId, taskPath, async () => {
      const current = await reconcileServiceTaskLifecycleSerialized(
        ctx,
        workspaceId,
        taskPath
      );
      if (current.state !== "waiting" || current.executionSessionId !== boundSessionId || !isSessionUnavailableParkedWait(current)) {
        throw new RpcError(
          RPC_LIFECYCLE,
          "task.startSession: exact session_unavailable wait changed before resume",
          {
            code: "TASK_SESSION_BIND_CAS_FAILED",
            taskPath,
            sessionId: boundSessionId,
            state: current.state,
            currentSessionId: current.executionSessionId,
            waitCode: current.wait?.code
          }
        );
      }
      await assertNoPendingResultDraftBeforeManagedSessionResume(ctx, workspaceId, taskPath);
      return resumeTaskForManagedSessionOperation(
        ctx,
        workspaceId,
        taskPath,
        "task.startSession.resume"
      );
    });
  }
  const callerBootstrapAppend = bootstrapPrompt;
  if (task.executionSessionId) {
    const probe = await ctx.runtime.probe(boundSession.id);
    if (probe.isAlive && boundSession.state !== "external") {
      return {
        kind: "reuse",
        connectionId,
        result: projectStartSessionResult(workspaceId, taskPath, task, boundSession, {
          cwd: task.worktree || mount.workspaceRoot
        })
      };
    }
  }
  return {
    kind: "launch",
    workspaceId,
    taskPath,
    connectionId,
    task,
    // Caller text is an optional dynamic append only — never the managed bootstrap itself.
    bootstrapPrompt: callerBootstrapAppend
  };
}
async function launchAndBindTaskStartSession(ctx, prepared) {
  const { workspaceId, taskPath, connectionId } = prepared;
  const mount = ctx.host.require(workspaceId);
  let task = await runTaskLifecycle(workspaceId, taskPath, async () => {
    const current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
    if (current.state !== "running") {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.startSession requires running at provider launch; got ${current.state}`,
        { code: "INVALID_TASK_STATE", state: current.state, taskPath }
      );
    }
    if (!current.executionSessionId) {
      throw new RpcError(
        -32602,
        "task.startSession Task lost its exact Session binding",
        { taskPath, connectionId }
      );
    }
    const withLane = await ensureTaskWorkspaceLane(ctx, workspaceId, current);
    return withLane;
  });
  task = await ensureTaskWorkspaceLane(ctx, workspaceId, task);
  const cwd = task.worktree || mount.workspaceRoot;
  const workspaceLane = task.workspace || task.worktree || task.branch ? {
    workspace: task.workspace || mount.workspaceRoot,
    worktree: task.worktree || mount.workspaceRoot,
    branch: task.branch || "HEAD",
    targetBranch: task.targetBranch
  } : void 0;
  const priorSessionId = task.executionSessionId.trim();
  let priorSession;
  let resumePrior = false;
  let firstStart = false;
  priorSession = await ctx.runtime.registry.read(priorSessionId) ?? void 0;
  if (!priorSession) {
    await parkTaskForUnavailableSession(ctx, {
      workspaceId,
      taskPath,
      sessionId: priorSessionId,
      reason: "task.startSession.bound-session-missing"
    });
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound Session not found: ${priorSessionId}; use task.replaceSession for an explicit fresh Session`,
      { code: "BOUND_SESSION_MISSING", taskPath, sessionId: priorSessionId }
    );
  }
  const exactTaskId = task.id;
  if (priorSession.workspace !== workspaceId || priorSession.connectionId !== connectionId || priorSession.currentTaskId !== exactTaskId) {
    await parkTaskForUnavailableSession(ctx, {
      workspaceId,
      taskPath,
      sessionId: priorSessionId,
      reason: "task.startSession.bound-session-identity-mismatch",
      detail: `Session binding mismatch (connectionId=${priorSession.connectionId}, currentTaskId=${priorSession.currentTaskId ?? "missing"})`
    });
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound Session ${priorSessionId} does not match the exact Task/Connection; use task.replaceSession for an explicit fresh Session`,
      {
        code: "BOUND_SESSION_IDENTITY_MISMATCH",
        taskPath,
        sessionId: priorSessionId,
        connectionId
      }
    );
  }
  firstStart = priorSession.state === "reserved";
  if (!firstStart) {
    const probe = await ctx.runtime.probe(priorSessionId);
    if (probe.isAlive || !probe.canResume) {
      await parkTaskForUnavailableSession(ctx, {
        workspaceId,
        taskPath,
        sessionId: priorSessionId,
        reason: "task.startSession.native-resume-unavailable"
      });
      throw new RpcError(
        RPC_LIFECYCLE,
        `Bound Session ${priorSessionId} cannot be resumed; use task.replaceSession for an explicit fresh Session`,
        {
          code: "BOUND_SESSION_NOT_RESUMABLE",
          taskPath,
          sessionId: priorSessionId,
          isAlive: probe.isAlive,
          canResume: probe.canResume
        }
      );
    }
    resumePrior = true;
  }
  const connectionSnapshot = priorSession.connectionSnapshot;
  if (!connectionSnapshot) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Bound Session ${priorSessionId} has no immutable Agent Connection snapshot`,
      { code: "BOUND_SESSION_SNAPSHOT_MISSING", taskPath, sessionId: priorSessionId }
    );
  }
  let stableBundle;
  try {
    stableBundle = await collectStableContextGeneration({
      workspaceRoot: mount.workspaceRoot,
      workspaceIdentity: workspaceId,
      packageRoot: ctx.packageRoot,
      packageVersion: ctx.version,
      task,
      session: priorSession,
      fs: mount.env.fs
    });
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(
      -32e3,
      `task.startSession contextGeneration collection failed: ${message2}`,
      { code: "CONTEXT_GENERATION_COLLECT_FAILED", taskPath, connectionId }
    );
  }
  const liveGeneration = stableBundle.contextGeneration;
  if (!liveGeneration) {
    throw new RpcError(
      -32e3,
      "task.startSession produced empty contextGeneration (fail closed)",
      { code: "CONTEXT_GENERATION_EMPTY", taskPath }
    );
  }
  const sessionContextGeneration = resumePrior && priorSession?.contextGeneration?.trim() === liveGeneration ? liveGeneration : void 0;
  let sessionBootstrap = await buildSessionBootstrapPrompt(
    ctx,
    task,
    {
      workspaceRoot: mount.workspaceRoot,
      systemRoot: mount.systemRoot,
      sessionContextGeneration,
      currentContextGeneration: liveGeneration
    },
    mount.env.fs
  );
  sessionBootstrap = appendCallerBootstrapSection(
    sessionBootstrap,
    prepared.bootstrapPrompt
  );
  const bootstrapImageRefs = await collectTaskBootstrapImageRefs(task);
  const bootstrapImageSystemRoot = bootstrapImageRefs.length > 0 ? mount.systemRoot : void 0;
  const bindSnapshot = await runTaskLifecycle(workspaceId, taskPath, async () => {
    const current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
    assertTaskSessionBindSnapshot(
      "task.startSession",
      taskPath,
      current,
      captureTaskSessionBindSnapshot(task)
    );
    task = current;
    return captureTaskSessionBindSnapshot(current);
  });
  let handle;
  try {
    if (resumePrior) {
      handle = await ctx.runtime.resumeSession({
        sessionId: priorSessionId,
        runtimeWorkspace: { cwd },
        cwd,
        bootstrapPrompt: sessionBootstrap,
        ...bootstrapImageRefs.length > 0 ? {
          bootstrapImageRefs,
          bootstrapImageSystemRoot
        } : {},
        currentTaskId: task.id
      });
    } else if (firstStart) {
      handle = await ctx.runtime.startSession({
        sessionId: priorSessionId,
        workspaceLane,
        runtimeWorkspace: { cwd },
        cwd,
        bootstrapPrompt: sessionBootstrap,
        ...bootstrapImageRefs.length > 0 ? {
          bootstrapImageRefs,
          bootstrapImageSystemRoot
        } : {},
        workspace: workspaceId
      });
    } else {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Bound Session ${priorSessionId} is neither reserved nor resumable`,
        { code: "BOUND_SESSION_NOT_STARTABLE", taskPath, sessionId: priorSessionId }
      );
    }
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    if (resumePrior) {
      await parkTaskForUnavailableSession(ctx, {
        workspaceId,
        taskPath,
        sessionId: priorSessionId,
        reason: "task.startSession.native-resume-failed",
        detail: message2
      });
      throw new RpcError(
        RPC_LIFECYCLE,
        `Bound Session ${priorSessionId} could not resume; Task remains waiting for explicit task.replaceSession`,
        {
          code: "BOUND_SESSION_RESUME_FAILED",
          taskPath,
          sessionId: priorSessionId,
          detail: message2,
          recoverable: true
        }
      );
    }
    await parkTaskForUnavailableSession(ctx, {
      workspaceId,
      taskPath,
      sessionId: priorSessionId,
      reason: "task.startSession.first-launch-failed",
      detail: message2
    });
    throw new RpcError(RPC_LIFECYCLE, message2, {
      code: "SESSION_LAUNCH_FAILED",
      taskPath,
      sessionId: priorSessionId,
      recoverable: true
    });
  }
  let bound;
  try {
    if (afterManagedSessionProviderStartForTests) {
      await afterManagedSessionProviderStartForTests({
        operation: "task.startSession",
        workspaceId,
        taskPath,
        sessionId: handle.sessionId
      });
    }
    bound = await runTaskLifecycle(
      workspaceId,
      taskPath,
      () => ctx.mutations.run(workspaceId, async () => {
        const current = await reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath);
        assertTaskSessionBindSnapshot("task.startSession", taskPath, current, bindSnapshot);
        ctx.host.markSelfWrite(workspaceId);
        const next = await patchTaskRecord(mount.env.fs, taskPath, {
          executionSessionId: handle.sessionId,
          contextGeneration: liveGeneration,
          updatedAt: mount.env.clock.now()
        });
        const generation = liveGeneration;
        try {
          await ctx.runtime.registry.update(handle.sessionId, {
            contextGeneration: generation
          });
        } catch {
        }
        emitTaskState(ctx, workspaceId, next, "task.startSession");
        ctx.events.emit(
          "session.state",
          workspaceId,
          {
            sessionId: handle.sessionId,
            state: handle.state,
            connectionId: handle.connectionId,
            taskPath,
            reason: resumePrior ? "task.startSession.resume" : "task.startSession"
          },
          "self"
        );
        return next;
      })
    );
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    const stopped = await stopUnboundManagedSession(
      ctx,
      handle.sessionId,
      "task.startSession",
      message2
    );
    if (isTaskSessionBindCasError(err)) {
      const data = err.data;
      throw new RpcError(RPC_LIFECYCLE, message2, {
        ...data,
        orphanSessionId: handle.sessionId,
        cleanupStopped: stopped
      });
    }
    throw new RpcError(RPC_LIFECYCLE, `task.startSession failed to bind managed Session: ${message2}`, {
      code: "TASK_SESSION_BIND_FAILED",
      taskPath,
      orphanSessionId: handle.sessionId,
      cleanupStopped: stopped
    });
  }
  await scheduleRetryableTaskInputsAfterSessionBind(ctx, {
    workspaceId,
    taskPath
  });
  return projectStartSessionResult(workspaceId, taskPath, bound, {
    id: handle.sessionId,
    connectionId: handle.connectionId,
    adapterId: handle.adapterId,
    state: handle.state,
    roleId: handle.roleId,
    runtimeWorkspace: handle.runtimeWorkspace
  }, { cwd });
}
var REPLACE_SESSION_RESTORE_REASON = "task.replaceSession.fresh";
async function taskReplaceSessionRpc(ctx, p) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath", "callerKind", "force"]),
    "task.replaceSession"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const taskPath = requireString(p, "taskPath");
  const callerKind = parseCallerKind(optionalString2(p, "callerKind") ?? "user");
  if ("force" in p && p.force !== void 0 && p.force !== false) {
    throw new RpcError(-32602, "task.replaceSession does not support force; wait for isTurnActive=false and retry", {
      code: "FORCE_NOT_SUPPORTED"
    });
  }
  const route = await readExactManagedSessionRoute(ctx, workspaceId, taskPath);
  return runManagedSessionFlight(
    workspaceId,
    taskPath,
    route,
    "replaceSession",
    async () => {
      const prepared = await prepareAuthorizedTaskStartSession(
        ctx,
        p,
        workspaceId,
        taskPath,
        callerKind,
        { skipReuseAndLaunchPrep: true }
      );
      if (prepared.kind !== "launch") {
        throw new RpcError(
          RPC_LIFECYCLE,
          "task.replaceSession could not prepare a fresh managed Session"
        );
      }
      assertManagedSessionRouteUnchanged("task.replaceSession", taskPath, route, prepared);
      return executeTaskReplaceSession(ctx, workspaceId, taskPath, route.connectionId);
    }
  );
}
async function assertReplaceSessionEligible(ctx, workspaceId, taskPath, task) {
  if (task.state !== "running" && task.state !== "waiting") {
    throw new RpcError(RPC_LIFECYCLE, `task.replaceSession requires running or waiting; got ${task.state}`, {
      code: "INVALID_TASK_STATE",
      state: task.state
    });
  }
  if (task.state === "waiting" && !isSessionUnavailableParkedWait(task)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "task.replaceSession allows waiting only with durable waitCode=session_unavailable; resolve user-input or tool waits first",
      {
        code: "REPLACE_SESSION_WAIT_NOT_ELIGIBLE",
        state: task.state,
        waitReason: task.wait?.reason,
        waitCode: task.wait?.code
      }
    );
  }
  const priorSessionId = task.executionSessionId?.trim() || "";
  if (!priorSessionId) {
    throw new RpcError(RPC_LIFECYCLE, "task.replaceSession requires a bound managed sessionId on the task", {
      code: "NO_BOUND_SESSION",
      taskPath
    });
  }
  const prior = await ctx.runtime.registry.read(priorSessionId);
  if (!prior || !prior.connectionId || prior.workspace !== workspaceId || prior.currentTaskId !== task.id) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "task.replaceSession requires an exact managed Session binding",
      { code: "BOUND_SESSION_IDENTITY_MISMATCH", taskPath, sessionId: priorSessionId }
    );
  }
  try {
    if ((await ctx.runtime.probe(priorSessionId)).isTurnActive === true) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `task.replaceSession refused: managed session ${priorSessionId} still has an in-flight turn (isTurnActive); retry when the turn settles`,
        { code: "TURN_BUSY", sessionId: priorSessionId, taskPath, isTurnActive: true }
      );
    }
  } catch (err) {
    if (err instanceof RpcError) throw err;
    if (!/Session not found/i.test(err instanceof Error ? err.message : String(err))) throw err;
  }
}
async function assertNoPendingResultDraftBeforeManagedSessionResume(ctx, workspaceId, taskPath) {
  const draft = await ctx.managedTaskResultReportDrafts.get(workspaceId, taskPath);
  if (!draft) return;
  const outcome = parseTaskOutcomeReport(draft.assistantText);
  if (outcome?.outcome === "blocked") return;
  throw new RpcError(
    RPC_LIFECYCLE,
    "task.startSession cannot resume while a pending managed result report draft exists; call task.resume to retry its publication",
    {
      code: "MANAGED_RESULT_DRAFT_REQUIRES_RESUME",
      taskPath,
      retryable: true
    }
  );
}
function resumeTaskForManagedSessionOperation(ctx, workspaceId, taskPath, reason) {
  const mount = ctx.host.require(workspaceId);
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const resumed = await taskResume(mount.env, taskPath);
    emitTaskState(ctx, workspaceId, resumed, reason);
    return resumed;
  });
}
async function executeTaskReplaceSession(ctx, workspaceId, taskPath, connectionId) {
  const mount = ctx.host.require(workspaceId);
  let task = await runTaskLifecycle(workspaceId, taskPath, async () => {
    let current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
    await assertReplaceSessionEligible(ctx, workspaceId, taskPath, current);
    if (current.state === "waiting") {
      current = await resumeTaskForManagedSessionOperation(
        ctx,
        workspaceId,
        taskPath,
        "task.replaceSession.resume"
      );
    }
    return ensureTaskWorkspaceLane(ctx, workspaceId, current);
  });
  const priorSessionId = task.executionSessionId.trim();
  const preserved = {
    taskId: task.id,
    nodeContextJson: JSON.stringify({
      nodeIds: task.nodeIds,
      nodeSnapshots: task.nodeSnapshots
    }),
    worktree: task.worktree,
    branch: task.branch,
    acceptMode: task.acceptMode,
    roleId: task.assigneeRoleId
  };
  let replacementSessionId;
  const parkAfterRetirement = async (detail) => {
    await parkTaskForUnavailableSession(ctx, {
      workspaceId,
      taskPath,
      sessionId: replacementSessionId ?? priorSessionId,
      reason: "task.replaceSession.failed",
      detail
    });
  };
  try {
    const cwd = task.worktree || mount.workspaceRoot;
    const workspaceLane = task.workspace || task.worktree || task.branch ? {
      workspace: task.workspace || mount.workspaceRoot,
      worktree: task.worktree || mount.workspaceRoot,
      branch: task.branch || "HEAD",
      targetBranch: task.targetBranch
    } : void 0;
    task = await runTaskLifecycle(workspaceId, taskPath, async () => {
      const current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
      assertTaskSessionBindSnapshot(
        "task.replaceSession",
        taskPath,
        current,
        captureTaskSessionBindSnapshot(task)
      );
      return current;
    });
    replacementSessionId = makeSessionId();
    await ctx.runtime.reserveSession({
      sessionId: replacementSessionId,
      connectionId,
      currentTaskId: task.id,
      workspace: workspaceId,
      workspaceLane,
      runtimeWorkspace: { cwd }
    });
    task = await runTaskLifecycle(workspaceId, taskPath, async () => {
      const current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
      assertTaskSessionBindSnapshot(
        "task.replaceSession",
        taskPath,
        current,
        captureTaskSessionBindSnapshot(task)
      );
      return current;
    });
    try {
      await ctx.toolApprovals.cancelSession(priorSessionId, "denied");
    } catch {
    }
    try {
      const priorProbe = await ctx.runtime.probe(priorSessionId);
      if (priorProbe.isAlive || SessionRegistry.isNonTerminal(priorProbe.state)) {
        await ctx.runtime.stopSession(priorSessionId, "user");
      } else {
        try {
          await ctx.runtime.registry.update(priorSessionId, {
            stopReason: "user",
            state: priorProbe.state === "failed" || priorProbe.state === "stopped" ? priorProbe.state : "stopped"
          });
        } catch {
        }
      }
    } catch (err) {
      if (!/Session not found/i.test(err instanceof Error ? err.message : String(err))) throw err;
    }
    const bootstrapImageRefs = await collectTaskBootstrapImageRefs(task);
    task = await runTaskLifecycle(workspaceId, taskPath, async () => {
      let current = await reconcileServiceTaskLifecycleSerialized(ctx, workspaceId, taskPath);
      if (current.state === "waiting" && current.executionSessionId === priorSessionId && isSessionUnavailableParkedWait(current)) {
        current = await resumeTaskForManagedSessionOperation(
          ctx,
          workspaceId,
          taskPath,
          "task.replaceSession.resume"
        );
      }
      assertTaskSessionBindSnapshot(
        "task.replaceSession",
        taskPath,
        current,
        captureTaskSessionBindSnapshot(task)
      );
      return current;
    });
    const replacementSession = await ctx.runtime.registry.read(replacementSessionId);
    if (!replacementSession) {
      throw new RpcError(RPC_LIFECYCLE, "Reserved replacement Session disappeared", {
        code: "REPLACE_SESSION_RESERVATION_MISSING",
        sessionId: replacementSessionId
      });
    }
    const replacementTask = {
      ...task,
      executionSessionId: replacementSessionId
    };
    const stableBundle = await collectStableContextGeneration({
      workspaceRoot: mount.workspaceRoot,
      workspaceIdentity: workspaceId,
      packageRoot: ctx.packageRoot,
      packageVersion: ctx.version,
      task: replacementTask,
      session: replacementSession,
      fs: mount.env.fs
    });
    const liveGeneration = stableBundle.contextGeneration;
    task = await managedTaskInputQueue.run(
      managedTaskInputQueueKey(workspaceId, taskPath),
      async () => {
        const beforePrebind = task;
        const prebound = await runTaskLifecycle(
          workspaceId,
          taskPath,
          () => ctx.mutations.run(workspaceId, async () => {
            const current = await reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath);
            assertTaskSessionBindSnapshot(
              "task.replaceSession",
              taskPath,
              current,
              captureTaskSessionBindSnapshot(beforePrebind)
            );
            ctx.host.markSelfWrite(workspaceId);
            const next = await patchTaskRecord(mount.env.fs, taskPath, {
              executionSessionId: replacementSessionId,
              state: "running",
              wait: null,
              contextGeneration: liveGeneration,
              updatedAt: mount.env.clock.now()
            });
            emitTaskState(ctx, workspaceId, next, "task.replaceSession.prebind");
            return next;
          })
        );
        try {
          await ctx.taskInputs.rebindOpenSessions(
            workspaceId,
            taskPath,
            replacementSessionId
          );
          return prebound;
        } catch (rebindError) {
          const detail = rebindError instanceof Error ? rebindError.message : String(rebindError);
          try {
            if (beforeReplaceTaskInputRollbackForTests) {
              await beforeReplaceTaskInputRollbackForTests({
                workspaceId,
                taskPath,
                priorSessionId,
                replacementSessionId
              });
            }
            await runTaskLifecycle(
              workspaceId,
              taskPath,
              () => ctx.mutations.run(workspaceId, async () => {
                const current = await loadTaskRecord(mount.env.fs, taskPath);
                if (current.executionSessionId !== replacementSessionId) {
                  throw new RpcError(
                    RPC_LIFECYCLE,
                    "task.replaceSession Task changed before TaskInput rebind rollback",
                    {
                      code: "TASK_SESSION_BIND_CAS_FAILED",
                      taskPath,
                      expectedSessionId: replacementSessionId,
                      actualSessionId: current.executionSessionId
                    }
                  );
                }
                ctx.host.markSelfWrite(workspaceId);
                const rolledBack = await patchTaskRecord(mount.env.fs, taskPath, {
                  executionSessionId: priorSessionId,
                  state: "waiting",
                  wait: {
                    reason: "external",
                    summary: SESSION_UNAVAILABLE_WAIT_SUMMARY,
                    code: SESSION_UNAVAILABLE_WAIT_CODE
                  },
                  updatedAt: mount.env.clock.now()
                });
                emitTaskState(
                  ctx,
                  workspaceId,
                  rolledBack,
                  "task.replaceSession.input-rebind-rollback"
                );
                return rolledBack;
              })
            );
          } catch (rollbackError) {
            throw new RpcError(
              RPC_LIFECYCLE,
              `task.replaceSession TaskInput rebind failed and Task rollback was incomplete: ${detail}`,
              {
                code: "REPLACE_SESSION_TASK_INPUT_REBIND_ROLLBACK_FAILED",
                taskPath,
                priorSessionId,
                newSessionId: replacementSessionId,
                rebindError: detail,
                rollbackError: rollbackError instanceof Error ? rollbackError.message : String(rollbackError)
              }
            );
          }
          throw new RpcError(
            RPC_LIFECYCLE,
            `task.replaceSession TaskInput rebind failed before provider start: ${detail}`,
            {
              code: "REPLACE_SESSION_TASK_INPUT_REBIND_FAILED",
              taskPath,
              priorSessionId,
              newSessionId: replacementSessionId,
              rolledBackToSessionId: priorSessionId
            }
          );
        }
      }
    );
    const prelaunchSnapshot = captureTaskSessionBindSnapshot(task);
    const handle = await ctx.runtime.startSession({
      sessionId: replacementSessionId,
      workspaceLane,
      runtimeWorkspace: { cwd },
      cwd,
      bootstrapPrompt: await buildFreshReplaceSessionBootstrap(ctx, task, {
        workspaceRoot: mount.workspaceRoot,
        systemRoot: mount.systemRoot,
        priorSessionId,
        currentContextGeneration: liveGeneration,
        roleFs: mount.env.fs
      }),
      ...bootstrapImageRefs.length > 0 ? { bootstrapImageRefs, bootstrapImageSystemRoot: mount.systemRoot } : {},
      workspace: workspaceId
    });
    if (afterManagedSessionProviderStartForTests) {
      await afterManagedSessionProviderStartForTests({
        operation: "task.replaceSession",
        workspaceId,
        taskPath,
        sessionId: handle.sessionId
      });
    }
    const bound = await runTaskLifecycle(
      workspaceId,
      taskPath,
      () => ctx.mutations.run(workspaceId, async () => {
        const current = await reconcileServiceTaskLifecycle(ctx, workspaceId, taskPath);
        assertTaskSessionPostStartOwnership(
          "task.replaceSession",
          taskPath,
          current,
          prelaunchSnapshot
        );
        ctx.events.emit(
          "session.state",
          workspaceId,
          {
            sessionId: handle.sessionId,
            state: handle.state,
            connectionId: handle.connectionId,
            taskPath,
            reason: REPLACE_SESSION_RESTORE_REASON,
            providerContextRestored: false,
            priorSessionId,
            replacedSessionId: priorSessionId
          },
          "self"
        );
        return current;
      })
    );
    await ctx.runtime.registry.update(handle.sessionId, {
      providerContextRestored: false,
      restoreReason: REPLACE_SESSION_RESTORE_REASON,
      replacedSessionId: priorSessionId,
      contextGeneration: liveGeneration
    });
    try {
      const priorRow = await ctx.runtime.registry.read(priorSessionId);
      if (priorRow) {
        await ctx.runtime.registry.update(priorSessionId, {
          replacedBySessionId: handle.sessionId,
          lastError: `replaced by ${handle.sessionId} (${REPLACE_SESSION_RESTORE_REASON})`
        });
      }
    } catch {
    }
    const nodeContextJson = JSON.stringify({
      nodeIds: bound.nodeIds,
      nodeSnapshots: bound.nodeSnapshots
    });
    if (bound.id !== preserved.taskId || bound.assigneeRoleId !== preserved.roleId || bound.acceptMode !== preserved.acceptMode || bound.worktree !== preserved.worktree || bound.branch !== preserved.branch || nodeContextJson !== preserved.nodeContextJson) {
      throw new RpcError(RPC_LIFECYCLE, "task.replaceSession mutated task lane/Node context/identity", {
        code: "TASK_IDENTITY_DRIFT"
      });
    }
    await scheduleRetryableTaskInputsAfterSessionBind(ctx, {
      workspaceId,
      taskPath
    });
    return {
      workspaceId,
      taskPath,
      task: projectTask(bound),
      session: {
        sessionId: handle.sessionId,
        connectionId: handle.connectionId,
        adapterId: handle.adapterId,
        state: handle.state,
        cwd,
        providerContextRestored: false,
        restoreReason: REPLACE_SESSION_RESTORE_REASON,
        replacedSessionId: priorSessionId
      },
      priorSessionId,
      replaced: true
    };
  } catch (err) {
    let cleanupStopped;
    if (replacementSessionId) {
      cleanupStopped = await stopUnboundManagedSession(
        ctx,
        replacementSessionId,
        "task.replaceSession",
        err instanceof Error ? err.message : String(err)
      );
    }
    const message2 = err instanceof Error ? err.message : String(err);
    const replaceErrorCode = err instanceof RpcError ? err.data?.code : void 0;
    const alreadyRolledBackAfterInputRebind = replaceErrorCode === "REPLACE_SESSION_TASK_INPUT_REBIND_FAILED";
    let rollbackRecoveryError;
    if (replaceErrorCode === "REPLACE_SESSION_TASK_INPUT_REBIND_ROLLBACK_FAILED" && replacementSessionId) {
      const failedReplacementSessionId = replacementSessionId;
      try {
        await managedTaskInputQueue.run(
          managedTaskInputQueueKey(workspaceId, taskPath),
          async () => {
            const current = await loadTaskRecord(mount.env.fs, taskPath);
            if (current.executionSessionId === failedReplacementSessionId && (current.state === "running" || current.state === "waiting")) {
              await ctx.taskInputs.rebindOpenSessions(
                workspaceId,
                taskPath,
                failedReplacementSessionId
              );
            }
          }
        );
      } catch (recoveryError) {
        rollbackRecoveryError = recoveryError instanceof Error ? recoveryError.message : String(recoveryError);
      }
    }
    if (!isTaskSessionBindCasError(err) && !alreadyRolledBackAfterInputRebind) {
      await parkAfterRetirement(message2);
    }
    if (isTaskSessionBindCasError(err)) {
      const data = err.data;
      throw new RpcError(RPC_LIFECYCLE, message2, {
        ...data,
        ...replacementSessionId ? { orphanSessionId: replacementSessionId } : {},
        ...cleanupStopped !== void 0 ? { cleanupStopped } : {}
      });
    }
    if (err instanceof RpcError) {
      if (rollbackRecoveryError) {
        throw new RpcError(RPC_LIFECYCLE, message2, {
          ...err.data ?? {},
          rollbackRecoveryError,
          ...cleanupStopped !== void 0 ? { cleanupStopped } : {}
        });
      }
      throw err;
    }
    throw new RpcError(RPC_LIFECYCLE, `task.replaceSession failed to start replacement session: ${message2}`, {
      code: "REPLACE_SESSION_LAUNCH_FAILED",
      taskPath,
      priorSessionId,
      ...replacementSessionId ? { sessionId: replacementSessionId } : {}
    });
  }
}
async function buildFreshReplaceSessionBootstrap(ctx, task, roots) {
  const base = await buildSessionBootstrapPrompt(
    ctx,
    task,
    {
      workspaceRoot: roots.workspaceRoot,
      systemRoot: roots.systemRoot,
      currentContextGeneration: roots.currentContextGeneration
    },
    roots.roleFs
  );
  const tail = [
    "--- Tent replace-session recovery ---",
    "providerContextRestored: false",
    "restoreReason: task.replaceSession.fresh",
    "Provider context was replaced explicitly. This is an independent managed Session on the same task/workspace lane.",
    "Do not invent prior chat/cache continuity. The canonical Task Package above remains authoritative.",
    `priorSessionId: ${roots.priorSessionId}`,
    "Pending TaskInputs and acceptMode are preserved on this Task. Final report still goes through TaskResult only."
  ].join("\n");
  return `${base}

${tail}
`;
}
async function taskList(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tasks = await loadTaskRecords(mount.env.fs);
  return {
    workspaceId,
    tasks: tasks.map(projectTask)
  };
}
async function taskGet(ctx, p) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["workspaceId", "taskPath"]), "task.get");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const task = await loadTaskRecord(mount.env.fs, taskPath);
  return { workspaceId, task: projectTask(task) };
}
async function taskPackage(ctx, p) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["workspaceId", "taskPath"]), "task.package");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const task = await loadTaskRecord(mount.env.fs, taskPath);
  return {
    workspaceId,
    taskPath: task.path,
    taskId: task.id,
    taskPackage: taskPackageForTask(task)
  };
}
async function workspaceCollaborationRpc(ctx, p) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["workspaceId", "nodeId"]), "workspace.collaboration");
  const workspaceId = requireWorkspaceId(ctx, p);
  const nodeId = optionalString2(p, "nodeId");
  const mount = ctx.host.require(workspaceId);
  let tent;
  let tasks;
  let results;
  let decisions;
  let roles;
  try {
    [tent, tasks, results, decisions, roles] = await Promise.all([
      nodeId ? loadTent(mount.env.fs) : Promise.resolve(void 0),
      loadTaskRecords(mount.env.fs),
      loadTaskResults(mount.env.fs),
      ctx.decisionRequests.listPending(workspaceId),
      loadRolesRegistry(mount.env.fs)
    ]);
  } catch (error) {
    const message2 = error instanceof Error ? error.message : String(error);
    throw new RpcError(
      -32e3,
      `workspace.collaboration failed to load authoritative facts: ${message2}`,
      { workspaceId, nodeId }
    );
  }
  let canonicalNodeId;
  if (nodeId) {
    const node2 = requireCanonicalNode(tent, nodeId);
    if (node2.invalid) {
      throw new RpcError(
        -32004,
        `Node is invalid and has no collaboration projection: ${node2.path}`,
        { nodeId: node2.id, path: node2.path, detail: node2.invalidReason }
      );
    }
    canonicalNodeId = node2.id;
  }
  return buildWorkspaceCollaborationProjection({
    workspaceId,
    ...canonicalNodeId ? { nodeId: canonicalNodeId } : {},
    tasks,
    results,
    pendingDecisions: decisions,
    roles: roles.roles,
    readSession: async (sessionId) => {
      const session = await ctx.runtime.registry.read(sessionId);
      return session ? {
        workspace: session.workspace,
        currentTaskId: session.currentTaskId,
        connectionId: session.connectionId,
        open: SessionRegistry.isOpen(session.state)
      } : null;
    },
    getConnection: (connectionId) => ctx.connectionCatalog.get(connectionId)
  });
}
async function taskResultList(ctx, p) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskId", "nodeId", "assigneeRoleId", "executionSessionId"]),
    "taskResult.list"
  );
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskId = optionalString2(p, "taskId");
  const nodeId = optionalString2(p, "nodeId");
  const assigneeRoleId = optionalString2(p, "assigneeRoleId");
  const executionSessionId = optionalString2(p, "executionSessionId");
  let results = await loadTaskResults(mount.env.fs, { taskId });
  let tasks;
  if (nodeId) {
    const tent = await loadTent(mount.env.fs);
    requireCanonicalNode(tent, nodeId);
    tasks = await loadTaskRecords(mount.env.fs);
    const taskNodeIds = new Map(
      tasks.map((task) => [task.id || task.path, new Set(taskReferencedNodeIds(task))])
    );
    results = results.filter((result) => taskNodeIds.get(result.taskId)?.has(nodeId));
  }
  if (assigneeRoleId || executionSessionId) {
    tasks ??= await loadTaskRecords(mount.env.fs);
    const taskById = new Map(tasks.map((task) => [task.id || task.path, task]));
    results = results.filter((result) => {
      const task = taskById.get(result.taskId);
      return (!assigneeRoleId || task?.assigneeRoleId === assigneeRoleId) && (!executionSessionId || task?.executionSessionId === executionSessionId);
    });
  }
  return { workspaceId, results: results.map(projectTaskResult) };
}
async function taskResultGet(ctx, p) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["workspaceId", "id"]), "taskResult.get");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const id = requireString(p, "id");
  const results = await loadTaskResults(mount.env.fs);
  const matches = results.filter((result) => result.id === id);
  if (matches.length !== 1) {
    throw new RpcError(
      matches.length === 0 ? -32004 : RPC_LIFECYCLE,
      `TaskResult id must identify exactly one record: ${id}`,
      { code: "TASK_RESULT_ID_NOT_UNIQUE", id, matches: matches.length }
    );
  }
  const found = matches[0];
  return { workspaceId, result: projectTaskResult(found) };
}
async function graphProjectionRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const tent = await loadTent(mount.env.fs);
  return buildGraphProjection(workspaceId, tent);
}
function buildGraphProjection(workspaceId, tent) {
  const nodes = [];
  const parentEdges = [];
  const markdownEdges = [];
  const wikiEdges = [];
  const relationEdges = [];
  const visit = (node2, parentNodeId) => {
    nodes.push(projectGraphNodeSummary(node2));
    parentEdges.push({ parentNodeId, childNodeId: node2.id });
    for (const child of node2.children) visit(child, node2.id);
  };
  for (const root of tent.roots) visit(root, null);
  const nodeIndex = indexFromNodes(tent.byId.values());
  const emitLinks = (node2) => {
    const notePath = nodeNotePath(node2.path);
    for (const link3 of extractOutLinksDetailed(node2.body)) {
      if (link3.kind === "artifact") continue;
      const resolved = resolveOutLink(nodeIndex, link3, notePath);
      const edge = {
        fromNodeId: node2.id,
        raw: link3.raw
      };
      if (link3.label) edge.label = link3.label;
      const targetNode = (resolved.targetPath ? tent.byPath.get(resolved.targetPath) : void 0) ?? (resolved.targetNodeId ? tent.byId.get(resolved.targetNodeId) : void 0);
      if (resolved.kind === "unresolved" || !targetNode) {
        const target = link3.targetPath ?? resolved.targetPath ?? (resolved.targetNodeId && resolved.targetNodeId !== link3.raw ? resolved.targetNodeId : void 0);
        edge.unresolved = target ? { raw: link3.raw, target } : { raw: link3.raw };
      } else {
        edge.toNodeId = targetNode.id;
      }
      if (link3.kind === "wiki") wikiEdges.push(edge);
      else markdownEdges.push(edge);
    }
    for (const child of node2.children) emitLinks(child);
  };
  for (const root of tent.roots) emitLinks(root);
  const emitRelations = (node2) => {
    for (const rel of node2.relations) {
      const edge = {
        id: rel.id,
        fromNodeId: node2.id,
        kind: rel.kind,
        direction: rel.direction
      };
      if (rel.label !== void 0) edge.label = rel.label;
      if ("nodeId" in rel.target) {
        edge.toNodeId = rel.target.nodeId;
      } else {
        edge.unresolved = rel.target.unresolved;
      }
      relationEdges.push(edge);
    }
    for (const child of node2.children) emitRelations(child);
  };
  for (const root of tent.roots) emitRelations(root);
  return {
    workspaceId,
    nodes,
    edges: {
      parent: parentEdges,
      markdown: markdownEdges,
      wiki: wikiEdges,
      relation: relationEdges
    }
  };
}
function projectGraphNodeSummary(source) {
  const title = typeof source.fm.title === "string" ? source.fm.title : void 0;
  const node2 = {
    nodeId: source.id,
    etag: source.etag,
    path: source.path,
    name: source.name,
    type: source.type,
    tags: source.tags,
    mode: source.mode,
    archived: source.archived,
    invalid: source.invalid
  };
  if (title) node2.title = title;
  return node2;
}
async function proposalList(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = optionalString2(p, "nodeId");
  const statusRaw = optionalString2(p, "status") ?? "pending";
  if (statusRaw !== "pending" && statusRaw !== "accepted" && statusRaw !== "rejected" && statusRaw !== "all") {
    throw new RpcError(-32602, `Invalid proposal status filter: ${statusRaw}`);
  }
  let proposals = await loadProposals(mount.env.fs);
  if (nodeId) {
    requireCanonicalNode(await loadTent(mount.env.fs), nodeId);
    proposals = proposals.filter((item) => item.nodeId === nodeId);
  }
  if (statusRaw !== "all") {
    proposals = proposals.filter((item) => item.status === statusRaw);
  }
  return { proposals: proposals.map(projectProposal) };
}
async function proposalSubmit(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = requireString(p, "nodeId");
  const role = requireString(p, "role");
  const body = requireString(p, "body");
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    requireCanonicalNode(await loadTent(mount.env.fs), nodeId);
    const proposal = await submitProposal(mount.env.fs, mount.env.clock, role, nodeId, body);
    emitProposalUpdated(ctx, workspaceId, proposal, "proposal.submit");
    return { proposal: projectProposal(proposal) };
  });
}
async function proposalResolve(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const proposalPath2 = requireString(p, "path");
  const decision = requireString(p, "decision");
  if (decision !== "accept" && decision !== "reject") {
    throw new RpcError(-32602, `Invalid proposal decision: ${decision}`);
  }
  const actorRaw = optionalString2(p, "actor") ?? "user";
  if (actorRaw !== "user") {
    throw new RpcError(
      -32001,
      "proposal resolve is user-only; agent self-resolve is forbidden",
      { actor: actorRaw }
    );
  }
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    if (decision === "accept") {
      await acceptProposal(mount.env.fs, proposalPath2);
    } else {
      await rejectProposal(mount.env.fs, proposalPath2);
    }
    const proposal = await loadProposal(mount.env.fs, proposalPath2);
    emitProposalUpdated(
      ctx,
      workspaceId,
      proposal,
      decision === "accept" ? "proposal.accept" : "proposal.reject"
    );
    return { proposal: projectProposal(proposal) };
  });
}
function projectProposal(proposal) {
  return {
    path: proposal.path,
    nodeId: proposal.nodeId,
    role: proposal.role,
    status: proposal.status,
    createdAt: proposal.createdAt,
    body: proposal.body
  };
}
function emitProposalUpdated(ctx, workspaceId, proposal, reason) {
  ctx.events.emit(
    "proposal.updated",
    workspaceId,
    {
      path: proposal.path,
      nodeId: proposal.nodeId,
      role: proposal.role,
      status: proposal.status,
      reason
    },
    "self"
  );
}
async function sessionList(ctx, p) {
  const workspaceId = optionalString2(p, "workspaceId");
  const all2 = await ctx.runtime.registry.list();
  const projections = [];
  for (const rec of all2) {
    if (workspaceId && rec.workspace && rec.workspace !== workspaceId) continue;
    const probe = await ctx.runtime.probe(rec.id);
    projections.push({
      sessionId: rec.id,
      connectionId: rec.connectionId,
      adapterId: rec.adapterId,
      ...rec.acpSession ? { acpSession: cloneAcpSessionConfigSnapshot(rec.acpSession) } : {},
      state: probe.state,
      roleId: rec.roleId,
      isAlive: probe.isAlive,
      canResume: probe.canResume,
      ...rec.providerContextRestored !== void 0 ? { providerContextRestored: rec.providerContextRestored } : {},
      ...sessionReplaceAuditFields(rec),
      isTurnActive: probe.isTurnActive === true,
      currentTaskId: rec.currentTaskId,
      workspace: rec.workspace,
      externalKey: recordExternalKey(rec),
      createdAt: rec.createdAt,
      updatedAt: rec.updatedAt
    });
  }
  return { sessions: projections };
}
async function sessionGet(ctx, p) {
  const sessionId = requireString(p, "sessionId");
  const rec = await ctx.runtime.registry.read(sessionId);
  if (!rec) throw new RpcError(-32004, `Session not found: ${sessionId}`);
  const probe = await ctx.runtime.probe(sessionId);
  const projection = {
    sessionId: rec.id,
    connectionId: rec.connectionId,
    adapterId: rec.adapterId,
    ...rec.acpSession ? { acpSession: cloneAcpSessionConfigSnapshot(rec.acpSession) } : {},
    state: probe.state,
    roleId: rec.roleId,
    isAlive: probe.isAlive,
    canResume: probe.canResume,
    ...rec.providerContextRestored !== void 0 ? { providerContextRestored: rec.providerContextRestored } : {},
    ...sessionReplaceAuditFields(rec),
    isTurnActive: probe.isTurnActive === true,
    currentTaskId: rec.currentTaskId,
    workspace: rec.workspace,
    externalKey: recordExternalKey(rec),
    createdAt: rec.createdAt,
    updatedAt: rec.updatedAt
  };
  return { session: projection };
}
function sessionReplaceAuditFields(rec) {
  return {
    ...rec.restoreReason !== void 0 ? { restoreReason: rec.restoreReason } : {},
    ...rec.replacedSessionId !== void 0 ? { replacedSessionId: rec.replacedSessionId } : {},
    ...rec.replacedBySessionId !== void 0 ? { replacedBySessionId: rec.replacedBySessionId } : {}
  };
}
async function sessionEnter(ctx, p) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "sessionId", "roleId", "externalKey", "currentTaskId", "cwd"]),
    "session.enter"
  );
  const workspaceId = optionalString2(p, "workspaceId");
  if (workspaceId) ctx.host.require(workspaceId);
  const sessionId = optionalString2(p, "sessionId");
  const roleId = optionalString2(p, "roleId");
  const externalKey = optionalString2(p, "externalKey");
  const currentTaskId = optionalString2(p, "currentTaskId");
  const cwd = optionalString2(p, "cwd");
  let priorExternalId;
  if (sessionId) {
    const prior = await ctx.runtime.registry.read(sessionId);
    if (prior?.state === "external") priorExternalId = prior.id;
  } else if (externalKey) {
    const hit = await findExternalSessionByKey(ctx, externalKey, workspaceId);
    if (hit) priorExternalId = hit.id;
  }
  let handle;
  try {
    handle = await ctx.runtime.enterExternalSession({
      sessionId: sessionId || void 0,
      roleId: roleId || void 0,
      workspace: workspaceId || void 0,
      cwd: cwd || void 0,
      currentTaskId: currentTaskId || void 0,
      externalKey: externalKey || void 0
    });
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    if (/already active as managed/i.test(message2)) {
      throw new RpcError(RPC_LIFECYCLE, message2);
    }
    throw new RpcError(-32602, message2);
  }
  const rec = await ctx.runtime.registry.read(handle.sessionId);
  const probe = await ctx.runtime.probe(handle.sessionId);
  const session = {
    sessionId: handle.sessionId,
    connectionId: handle.connectionId,
    adapterId: handle.adapterId,
    state: probe.state,
    roleId: handle.roleId,
    isAlive: probe.isAlive,
    canResume: probe.canResume,
    ...rec?.providerContextRestored !== void 0 ? { providerContextRestored: rec.providerContextRestored } : {},
    currentTaskId: rec?.currentTaskId,
    workspace: rec?.workspace ?? workspaceId,
    externalKey: recordExternalKey(rec ?? {}) ?? externalKey,
    createdAt: handle.createdAt,
    updatedAt: handle.updatedAt
  };
  return {
    session,
    sessionToken: handle.sessionToken,
    reused: priorExternalId === handle.sessionId
  };
}
async function sessionStatus(ctx, p) {
  const sessionIdArg = optionalString2(p, "sessionId");
  const externalKey = optionalString2(p, "externalKey") || optionalString2(p, "key");
  const workspaceId = optionalString2(p, "workspaceId");
  if (workspaceId) ctx.host.require(workspaceId);
  if (!sessionIdArg && !externalKey) {
    const all2 = await ctx.runtime.registry.list();
    const sessions = [];
    for (const rec2 of all2) {
      if (rec2.state !== "external") continue;
      if (workspaceId && rec2.workspace && rec2.workspace !== workspaceId) continue;
      const probe2 = await ctx.runtime.probe(rec2.id);
      sessions.push({
        sessionId: rec2.id,
        connectionId: rec2.connectionId,
        adapterId: rec2.adapterId,
        state: probe2.state,
        roleId: rec2.roleId,
        isAlive: probe2.isAlive,
        canResume: probe2.canResume,
        ...rec2.providerContextRestored !== void 0 ? { providerContextRestored: rec2.providerContextRestored } : {},
        currentTaskId: rec2.currentTaskId,
        workspace: rec2.workspace,
        externalKey: recordExternalKey(rec2),
        createdAt: rec2.createdAt,
        updatedAt: rec2.updatedAt
      });
    }
    const incompleteTasks2 = workspaceId ? await listIncompleteTasksForSessions(
      ctx,
      workspaceId,
      sessions.map((s) => s.sessionId)
    ) : [];
    return { sessions, incompleteTasks: incompleteTasks2 };
  }
  const resolved = await resolveExternalSessionRef(ctx, {
    sessionId: sessionIdArg,
    externalKey,
    workspaceId
  });
  if (!resolved) {
    const label = sessionIdArg || externalKey || "?";
    throw new RpcError(-32004, `Session not found: ${label}`);
  }
  const { rec, sessionId } = resolved;
  const probe = await ctx.runtime.probe(sessionId);
  const session = {
    sessionId: rec.id,
    connectionId: rec.connectionId,
    adapterId: rec.adapterId,
    state: probe.state,
    roleId: rec.roleId,
    isAlive: probe.isAlive,
    canResume: probe.canResume,
    ...rec.providerContextRestored !== void 0 ? { providerContextRestored: rec.providerContextRestored } : {},
    currentTaskId: rec.currentTaskId,
    workspace: rec.workspace,
    externalKey: recordExternalKey(rec),
    createdAt: rec.createdAt,
    updatedAt: rec.updatedAt
  };
  const incompleteTasks = await listIncompleteTasksBoundToSession(
    ctx,
    rec.workspace || workspaceId,
    sessionId
  );
  return {
    session,
    incompleteTasks,
    /** Convenience: true while external is open or managed process is alive. */
    open: SessionRegistry.isOpen(probe.state) || probe.isAlive
  };
}
async function sessionLeave(ctx, p) {
  const sessionIdArg = optionalString2(p, "sessionId");
  const externalKey = optionalString2(p, "externalKey") || optionalString2(p, "key");
  const workspaceId = optionalString2(p, "workspaceId");
  if (workspaceId) ctx.host.require(workspaceId);
  if (!sessionIdArg && !externalKey) {
    throw new RpcError(
      -32602,
      "session.leave requires sessionId or externalKey"
    );
  }
  const resolved = await resolveExternalSessionRef(ctx, {
    sessionId: sessionIdArg,
    externalKey,
    workspaceId
  });
  if (!resolved) {
    return {
      sessionId: sessionIdArg || "",
      externalKey: externalKey || void 0,
      state: "stopped",
      left: false,
      alreadyLeft: true,
      incompleteTasks: []
    };
  }
  const { rec, sessionId } = resolved;
  let incompleteTasks = await listIncompleteTasksBoundToSession(
    ctx,
    rec.workspace || workspaceId,
    sessionId
  );
  if (SessionRegistry.isNonTerminal(rec.state)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `session.leave is for external/pull-host sessions; managed session is ${rec.state}. Use task.interrupt or runtime stop.`,
      { sessionId, state: rec.state }
    );
  }
  let left = false;
  let state = rec.state;
  if (rec.state === "external") {
    const leaveWorkspaceId = rec.workspace || workspaceId;
    const closeWithinMutation = async () => {
      const currentSession = await ctx.runtime.registry.read(sessionId);
      if (currentSession?.state !== "external") {
        state = currentSession?.state === "failed" ? "failed" : "stopped";
        return;
      }
      if (leaveWorkspaceId) {
        const mount = ctx.host.require(leaveWorkspaceId);
        const boundTasks = (await listIncompleteTasksBoundToSession(
          ctx,
          leaveWorkspaceId,
          sessionId
        )).sort((a, b) => a.path.localeCompare(b.path));
        ctx.host.markSelfWrite(leaveWorkspaceId, 200, TEMP_DIR);
        for (const boundTask of boundTasks) {
          const currentTask = await loadTaskRecord(mount.env.fs, boundTask.path);
          if (currentTask.executionSessionId?.trim() !== sessionId) continue;
          await applySessionUnavailablePark(
            ctx,
            leaveWorkspaceId,
            boundTask.path,
            "session.leave"
          );
        }
      }
      await ctx.runtime.stopSession(sessionId, "user");
      left = true;
      const after = await ctx.runtime.registry.read(sessionId);
      state = after?.state ?? "stopped";
    };
    if (leaveWorkspaceId) {
      await ctx.mutations.run(leaveWorkspaceId, closeWithinMutation);
    } else {
      await closeWithinMutation();
    }
    if (leaveWorkspaceId) {
      incompleteTasks = await listIncompleteTasksBoundToSession(
        ctx,
        leaveWorkspaceId,
        sessionId
      );
    }
  } else {
    state = rec.state === "failed" ? "failed" : "stopped";
  }
  return {
    sessionId,
    externalKey: recordExternalKey(rec) ?? externalKey,
    state,
    left,
    alreadyLeft: !left,
    incompleteTasks
    /**
     * Explicit contract: leave never auto-submits or accepts.
     * Callers must use task.submit / task.accept separately.
     */
  };
}
async function findExternalSessionByKey(ctx, externalKey, workspaceId) {
  const all2 = await ctx.runtime.registry.list();
  return all2.find(
    (rec) => rec.state === "external" && recordExternalKey(rec) === externalKey && (!workspaceId || !rec.workspace || rec.workspace === workspaceId)
  ) ?? null;
}
async function resolveExternalSessionRef(ctx, ref) {
  if (ref.sessionId) {
    if (!isSessionId2(ref.sessionId)) {
    } else {
      const rec = await ctx.runtime.registry.read(ref.sessionId);
      if (rec) return { rec, sessionId: rec.id };
    }
  }
  if (ref.externalKey) {
    const hit = await findExternalSessionByKey(ctx, ref.externalKey, ref.workspaceId);
    if (hit) return { rec: hit, sessionId: hit.id };
  }
  if (ref.sessionId && !isSessionId2(ref.sessionId)) {
    const hit = await findExternalSessionByKey(ctx, ref.sessionId, ref.workspaceId);
    if (hit) return { rec: hit, sessionId: hit.id };
  }
  return null;
}
async function listIncompleteTasksBoundToSession(ctx, workspaceId, sessionId) {
  if (!workspaceId) return [];
  try {
    ctx.host.require(workspaceId);
  } catch {
    return [];
  }
  return listIncompleteTasksForSessions(ctx, workspaceId, [sessionId]);
}
async function listIncompleteTasksForSessions(ctx, workspaceId, sessionIds) {
  if (sessionIds.length === 0) return [];
  const idSet = new Set(sessionIds);
  const mount = ctx.host.require(workspaceId);
  const tasks = await loadTaskRecords(mount.env.fs);
  const out = [];
  for (const task of tasks) {
    const sid = task.executionSessionId?.trim();
    if (!sid || !idSet.has(sid)) continue;
    if (!isActiveTaskState(task.state)) continue;
    out.push({
      path: task.path,
      id: task.id,
      state: task.state,
      roleId: task.assigneeRoleId,
      sessionId: sid
    });
  }
  return out;
}
async function toolApprovalListPending(ctx, p) {
  const workspaceId = optionalString2(p, "workspaceId");
  const pending = await ctx.toolApprovals.listPending(workspaceId);
  return { approvals: pending.map(projectToolApproval) };
}
async function toolApprovalGet(ctx, p) {
  const approvalId = requireString(p, "approvalId");
  const item = await ctx.toolApprovals.get(approvalId);
  if (!item) throw new RpcError(-32004, `Tool approval not found: ${approvalId}`);
  return { approval: projectToolApproval(item) };
}
async function toolApprovalResolve(ctx, p, decision) {
  const approvalId = requireString(p, "approvalId");
  const actorRaw = optionalString2(p, "actor") ?? "user";
  if (actorRaw !== "user") {
    throw new RpcError(
      -32001,
      "toolApproval resolve is user-only; agent self-approve is forbidden",
      { actor: actorRaw }
    );
  }
  const item = await ctx.toolApprovals.resolve(approvalId, decision, actorRaw);
  ctx.events.emit(
    "toolApproval.resolved",
    item.workspaceId,
    {
      approvalId: item.id,
      decision,
      actor: actorRaw,
      sessionId: item.sessionId,
      taskPath: item.taskPath,
      toolTitle: item.toolTitle
    },
    "self"
  );
  const hasPendingForSession = await ctx.toolApprovals.hasPendingForSession(
    item.sessionId
  );
  if (decision === "approved" && !hasPendingForSession && item.taskPath) {
    try {
      const pendingDecision = await ctx.decisionRequests.getPendingForTask(
        item.workspaceId,
        item.taskPath
      );
      if (!pendingDecision) {
        const mount = ctx.host.get(item.workspaceId);
        if (mount) {
          const task = await loadTaskRecord(mount.env.fs, item.taskPath);
          if (task.state === "waiting" && task.wait?.reason === "user-input") {
            await ctx.mutations.run(item.workspaceId, async () => {
              ctx.host.markSelfWrite(item.workspaceId);
              const resumed = await taskResume(mount.env, item.taskPath);
              emitTaskState(ctx, item.workspaceId, resumed, "toolApproval.approveOnce");
            });
          }
        }
      }
    } catch {
    }
  }
  return { approval: projectToolApproval(item) };
}
function projectToolApproval(item) {
  return {
    id: item.id,
    workspaceId: item.workspaceId,
    sessionId: item.sessionId,
    taskId: item.taskId,
    taskPath: item.taskPath,
    role: item.role,
    toolTitle: item.toolTitle,
    toolCallId: item.toolCallId,
    options: item.options,
    status: item.status,
    createdAt: item.createdAt,
    expiresAt: item.expiresAt,
    resolvedAt: item.resolvedAt,
    resolvedBy: item.resolvedBy
  };
}
async function decisionCallerAuthority(ctx, workspaceId, caller) {
  const session = await resolveDecisionCallerSession(ctx, caller);
  if (!session) return { kind: "user", id: "user" };
  if (session.workspace !== workspaceId || !session.roleId || !SessionRegistry.isOpen(session.state)) {
    throw new RpcError(-32001, "Caller Session is not an exact live Role authority", {
      code: "DECISION_RESPONDER_FORBIDDEN",
      sessionId: session.id,
      workspaceId
    });
  }
  return { kind: "role", id: session.roleId };
}
function assertDecisionAuthority(request, responder) {
  if (request.target.kind !== responder.kind || request.target.id !== responder.id) {
    throw new RpcError(-32001, "Caller is not the frozen Decision Request target", {
      code: "DECISION_RESPONDER_FORBIDDEN",
      requestId: request.id,
      target: request.target,
      responder
    });
  }
}
async function decisionRequestListPending(ctx, p, caller) {
  assertAllowedParams(p, /* @__PURE__ */ new Set(["workspaceId"]), "decisionRequest.listPending");
  const workspaceId = requireWorkspaceId(ctx, p);
  const responder = await decisionCallerAuthority(ctx, workspaceId, caller);
  const mounted = ctx.host.require(workspaceId);
  const authorized = (await ctx.decisionRequests.listPending(workspaceId)).filter(
    (request) => request.target.kind === responder.kind && request.target.id === responder.id
  );
  const pending = (await Promise.all(
    authorized.map(async (request) => {
      const task = await loadTaskRecord(mounted.env.fs, request.taskPath).catch(
        () => void 0
      );
      return task && task.id === request.taskId && !TERMINAL_TASK_STATES.has(task.state) ? request : void 0;
    })
  )).filter((request) => request !== void 0);
  return { requests: pending.map(projectDecisionRequest) };
}
async function interactionListPending(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  let requests;
  let toolApprovals;
  let taskResults;
  try {
    const settled = await Promise.all([
      ctx.decisionRequests.listPending(workspaceId),
      ctx.toolApprovals.listPending(workspaceId),
      loadTaskResults(mount.env.fs)
    ]);
    requests = settled[0];
    toolApprovals = settled[1];
    taskResults = settled[2];
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(
      -32e3,
      `interaction.listPending failed to load pending sources: ${message2}`,
      { workspaceId }
    );
  }
  let tasks = [];
  let tasksByPath = /* @__PURE__ */ new Map();
  let tasksById = /* @__PURE__ */ new Map();
  let taskRecordsById = /* @__PURE__ */ new Map();
  try {
    tasks = await loadTaskRecords(mount.env.fs);
    tasksByPath = new Map(tasks.map((t) => [t.path, t]));
    taskRecordsById = /* @__PURE__ */ new Map();
    for (const task of tasks) {
      if (!task.id) continue;
      const sameId = taskRecordsById.get(task.id) ?? [];
      sameId.push(task);
      taskRecordsById.set(task.id, sameId);
    }
    tasksById = new Map(
      [...taskRecordsById.entries()].filter(([, records]) => records.length === 1).map(([taskId, records]) => [taskId, records[0]])
    );
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(
      -32e3,
      `interaction.listPending failed to load task envelopes: ${message2}`,
      { workspaceId }
    );
  }
  const items = [];
  for (const request of requests.filter((item) => item.target.kind === "user")) {
    const task = tasksByPath.get(request.taskPath) ?? tasksById.get(request.taskId);
    if (!task || task.id !== request.taskId || TERMINAL_TASK_STATES.has(task.state)) {
      continue;
    }
    const item = {
      kind: "decisionRequest",
      id: request.id,
      workspaceId: request.workspaceId,
      createdAt: request.createdAt,
      taskPath: request.taskPath,
      taskId: request.taskId,
      sessionId: request.requester.id,
      ...task ? { role: taskParentRoleId(task) } : {},
      target: request.target,
      question: request.question,
      options: request.options.map((option) => ({ ...option }))
    };
    items.push(item);
  }
  for (const approval of toolApprovals) {
    const task = approval.taskPath ? tasksByPath.get(approval.taskPath) : approval.taskId ? tasksById.get(approval.taskId) : void 0;
    const projected = projectToolApproval(approval);
    const responsibleRole = projected.role ?? (task ? taskParentRoleId(task) : void 0);
    const item = {
      kind: "toolApproval",
      id: projected.id,
      workspaceId: projected.workspaceId,
      createdAt: projected.createdAt,
      sessionId: projected.sessionId,
      ...projected.taskPath ? { taskPath: projected.taskPath } : {},
      ...projected.taskId ? { taskId: projected.taskId } : task?.id ? { taskId: task.id } : {},
      ...responsibleRole ? { role: responsibleRole } : {},
      toolTitle: projected.toolTitle,
      options: projected.options.map((o) => ({
        optionId: o.optionId,
        ...o.kind ? { kind: o.kind } : {},
        ...o.name ? { name: o.name } : {}
      })),
      ...projected.expiresAt ? { expiresAt: projected.expiresAt } : {}
    };
    items.push(item);
  }
  const currentResultOwners = /* @__PURE__ */ new Map();
  const taskResultsById = /* @__PURE__ */ new Map();
  for (const result of taskResults) {
    const sameId = taskResultsById.get(result.id) ?? [];
    sameId.push(result);
    taskResultsById.set(result.id, sameId);
  }
  for (const task of tasks) {
    if (task.state !== "submitted" || task.requester?.kind !== "user") continue;
    if (task.requester.id !== "user" || !task.id || !task.currentResultId) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "User-reviewable Task has invalid Result authority",
        { code: "INTERACTION_RESULT_TASK_INVALID", taskId: task.id ?? null }
      );
    }
    const sameTaskId = taskRecordsById.get(task.id) ?? [];
    if (sameTaskId.length !== 1) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "User-reviewable Task identity is ambiguous",
        { code: "INTERACTION_RESULT_TASK_NOT_UNIQUE", taskId: task.id }
      );
    }
    const owners = currentResultOwners.get(task.currentResultId) ?? [];
    owners.push(task);
    currentResultOwners.set(task.currentResultId, owners);
  }
  for (const [resultId, owners] of currentResultOwners) {
    if (owners.length !== 1) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "User-reviewable Task Result identity is ambiguous",
        { code: "INTERACTION_RESULT_NOT_UNIQUE", resultId, matches: owners.length }
      );
    }
    const task = owners[0];
    const resultPath = taskResultPathForTask(task.path, resultId);
    const matchingResults = taskResultsById.get(resultId) ?? [];
    if (matchingResults.length !== 1) {
      throw new RpcError(
        RPC_LIFECYCLE,
        "User-reviewable Task Result identity is not unique",
        {
          code: "INTERACTION_RESULT_NOT_UNIQUE",
          taskId: task.id,
          resultId,
          matches: matchingResults.length
        }
      );
    }
    const result = matchingResults[0];
    if (result.path !== resultPath || result.id !== resultId || result.taskId !== task.id || result.status !== "ready") {
      throw new RpcError(
        RPC_LIFECYCLE,
        "User-reviewable Task Result does not match its exact Task authority",
        { code: "INTERACTION_RESULT_CHANGED", taskId: task.id, resultId }
      );
    }
    const item = {
      kind: "result",
      id: result.id,
      workspaceId,
      createdAt: result.createdAt,
      taskId: result.taskId,
      path: result.path,
      status: "ready",
      taskPath: task.path,
      ...task.executionSessionId ? { sessionId: task.executionSessionId } : {}
    };
    items.push(item);
  }
  items.sort(comparePendingInteraction);
  const counts = {
    decisionRequest: 0,
    toolApproval: 0,
    result: 0,
    total: items.length
  };
  for (const item of items) {
    counts[item.kind] += 1;
  }
  return { workspaceId, items, counts };
}
function comparePendingInteraction(a, b) {
  const byTime = a.createdAt.localeCompare(b.createdAt);
  if (byTime !== 0) return byTime;
  const byKind = a.kind.localeCompare(b.kind);
  if (byKind !== 0) return byKind;
  return a.id.localeCompare(b.id);
}
async function requireExactDecisionRequest(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const taskPath = requireString(p, "taskPath");
  const requestId = requireString(p, "requestId");
  const request = await ctx.decisionRequests.getExact(workspaceId, taskPath, requestId);
  if (!request) {
    throw new RpcError(-32004, `Decision Request not found for exact Task: ${requestId}`);
  }
  return { workspaceId, taskPath, request };
}
async function requireDecisionRequestById(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const requestId = requireString(p, "requestId");
  const request = await ctx.decisionRequests.getExactById(workspaceId, requestId);
  if (!request) {
    throw new RpcError(-32004, `Decision Request not found: ${requestId}`);
  }
  const mount = ctx.host.require(workspaceId);
  const tasks = await loadTaskRecords(mount.env.fs);
  const matches = tasks.filter(
    (task) => task.id === request.taskId
  );
  if (matches.length !== 1 || matches[0].path !== request.taskPath) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `Decision Request must identify exactly one canonical Task: ${requestId}`,
      {
        code: "DECISION_REQUEST_TASK_NOT_UNIQUE",
        requestId,
        taskId: request.taskId,
        matches: matches.length
      }
    );
  }
  return {
    workspaceId,
    taskPath: request.taskPath,
    request,
    task: matches[0]
  };
}
async function decisionRequestGet(ctx, p, caller) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath", "requestId"]),
    "decisionRequest.get"
  );
  const exact = await requireExactDecisionRequest(ctx, p);
  const responder = await decisionCallerAuthority(ctx, exact.workspaceId, caller);
  assertDecisionAuthority(exact.request, responder);
  if (exact.request.status === "pending") {
    const mount = ctx.host.require(exact.workspaceId);
    const task = await loadTaskRecord(mount.env.fs, exact.taskPath).catch(() => void 0);
    if (!task || task.id !== exact.request.taskId || TERMINAL_TASK_STATES.has(task.state)) {
      throw new RpcError(-32004, `Decision Request not found for active exact Task: ${exact.request.id}`);
    }
  }
  return { request: projectDecisionRequest(exact.request) };
}
function parseDecisionResponseParam(raw, options) {
  try {
    return validateDecisionResponse(raw, options);
  } catch (error) {
    throw new RpcError(-32602, error instanceof Error ? error.message : String(error));
  }
}
function assertAnsweredDecisionResponseInputMatches(existing, expected, currentTask) {
  for (const field of [
    "id",
    "workspaceId",
    "taskPath",
    "taskId",
    "role",
    "kind",
    "text"
  ]) {
    if (existing[field] !== expected[field]) {
      throw new RpcError(
        RPC_LIFECYCLE,
        `Answered Decision Request TaskInput conflicts on immutable field ${field}`,
        {
          code: "DECISION_RESPONSE_INPUT_MISMATCH",
          inputId: expected.id,
          field
        }
      );
    }
  }
  if ((existing.status === "pending" || existing.status === "processing" || existing.status === "failed") && existing.sessionId !== currentTask.executionSessionId) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Answered Decision Request TaskInput conflicts on current Session binding",
      {
        code: "DECISION_RESPONSE_INPUT_MISMATCH",
        inputId: expected.id,
        field: "sessionId"
      }
    );
  }
}
async function decisionRequestRespondRpc(ctx, p, caller) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "requestId", "response"]),
    "decisionRequest.respond"
  );
  const exact = await requireDecisionRequestById(ctx, p);
  const responder = await decisionCallerAuthority(ctx, exact.workspaceId, caller);
  assertDecisionAuthority(exact.request, responder);
  parseDecisionResponseParam(p.response, exact.request.options);
  const mount = ctx.host.require(exact.workspaceId);
  const result = await runTaskLifecycle(
    exact.workspaceId,
    exact.taskPath,
    () => ctx.mutations.run(exact.workspaceId, async () => {
      const current = await requireDecisionRequestById(ctx, p);
      if (current.taskPath !== exact.taskPath || current.request.id !== exact.request.id || current.request.taskId !== exact.request.taskId) {
        throw new RpcError(RPC_LIFECYCLE, "Decision Request Task identity changed", {
          code: "DECISION_TASK_MISMATCH",
          requestId: exact.request.id
        });
      }
      const currentRequest = current.request;
      assertDecisionAuthority(currentRequest, responder);
      const response = parseDecisionResponseParam(p.response, currentRequest.options);
      const task = current.task;
      const exactDecisionWaitCode = `decision_request:${currentRequest.id}`;
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const prepared = prepareDecisionResponse({
        request: coreDecisionRequest(currentRequest),
        responder,
        response,
        binding: {
          workspaceId: exact.workspaceId,
          taskPath: exact.taskPath,
          taskId: task.id,
          sessionId: currentRequest.requester.id,
          ...taskParentRoleId(task) ? { role: taskParentRoleId(task) } : {}
        },
        now
      });
      if (currentRequest.status === "answered") {
        const answered2 = await ctx.decisionRequests.answerExact({
          workspaceId: exact.workspaceId,
          taskPath: exact.taskPath,
          requestId: currentRequest.id,
          responder,
          response
        });
        const input2 = await ctx.taskInputs.get(
          prepared.taskInput.id,
          exact.workspaceId,
          exact.taskPath
        );
        if (!input2) {
          throw new RpcError(
            RPC_LIFECYCLE,
            "Answered Decision Request is missing its deterministic TaskInput",
            {
              code: "DECISION_RESPONSE_INPUT_MISSING",
              requestId: currentRequest.id,
              inputId: prepared.taskInput.id
            }
          );
        }
        assertAnsweredDecisionResponseInputMatches(input2, prepared.taskInput, task);
        let nextTask2 = task;
        if (task.state === "waiting" && task.wait?.reason === "user-input" && task.wait.code === exactDecisionWaitCode) {
          const inputSessionId = input2.sessionId;
          const session2 = inputSessionId ? await ctx.runtime.registry.read(inputSessionId) : null;
          const canResume = Boolean(
            inputSessionId && task.executionSessionId === inputSessionId && session2 && SessionRegistry.isOpen(session2.state) && session2.workspace === exact.workspaceId && session2.currentTaskId === task.id
          );
          if (canResume) {
            ctx.events.emit(
              "decisionRequest.resolved",
              exact.workspaceId,
              projectDecisionRequest(answered2),
              "self"
            );
            ctx.host.markSelfWrite(exact.workspaceId);
            nextTask2 = await taskResume(mount.env, exact.taskPath);
            emitTaskState(ctx, exact.workspaceId, nextTask2, "decisionRequest.respond.recover");
          }
        }
        let enqueue = nextTask2.state === "running" && (input2.status === "pending" || input2.status === "failed");
        if (enqueue) {
          const inputSessionId = input2.sessionId;
          const session2 = inputSessionId ? await ctx.runtime.registry.read(inputSessionId) : null;
          enqueue = Boolean(
            inputSessionId && nextTask2.executionSessionId === inputSessionId && session2 && SessionRegistry.isOpen(session2.state) && session2.workspace === exact.workspaceId && session2.currentTaskId === nextTask2.id
          );
        }
        return { answered: answered2, input: input2, task: nextTask2, enqueue };
      }
      if (TERMINAL_TASK_STATES.has(task.state)) {
        throw new RpcError(
          RPC_LIFECYCLE,
          "Cannot respond to a Decision Request for a terminal Task",
          {
            code: "DECISION_TASK_TERMINAL",
            requestId: currentRequest.id,
            taskId: task.id,
            state: task.state
          }
        );
      }
      if (task.state !== "waiting" || task.wait?.reason !== "user-input") {
        throw new RpcError(
          RPC_LIFECYCLE,
          "Pending Decision Request requires its exact Task to be waiting for user input",
          {
            code: "DECISION_TASK_NOT_WAITING_USER_INPUT",
            requestId: currentRequest.id,
            taskId: task.id,
            state: task.state,
            waitReason: task.wait?.reason
          }
        );
      }
      if (task.wait.code !== exactDecisionWaitCode) {
        throw new RpcError(
          RPC_LIFECYCLE,
          "Pending Decision Request does not own the Task's current user-input wait",
          {
            code: "DECISION_TASK_WAIT_IDENTITY_MISMATCH",
            requestId: currentRequest.id,
            waitCode: task.wait.code
          }
        );
      }
      if (task.executionSessionId !== currentRequest.requester.id) {
        throw new RpcError(RPC_LIFECYCLE, "Decision requester Session is no longer bound to the exact Task", {
          code: "DECISION_SESSION_MISMATCH",
          requestId: currentRequest.id,
          requesterSessionId: currentRequest.requester.id,
          taskSessionId: task.executionSessionId
        });
      }
      const session = await ctx.runtime.registry.read(currentRequest.requester.id);
      if (!session || !SessionRegistry.isOpen(session.state) || session.workspace !== exact.workspaceId || session.currentTaskId !== task.id) {
        throw new RpcError(RPC_LIFECYCLE, "Decision requester Session registry binding is stale", {
          code: "DECISION_SESSION_BINDING_STALE",
          requestId: currentRequest.id
        });
      }
      const existingInput = await ctx.taskInputs.get(
        prepared.taskInput.id,
        exact.workspaceId,
        exact.taskPath
      );
      const input = existingInput ? (assertDecisionResponseTaskInputMatches(existingInput, prepared.taskInput), existingInput) : await ctx.taskInputs.add(prepared.taskInput);
      const wasPending = currentRequest.status === "pending";
      const answered = await ctx.decisionRequests.answerExact({
        workspaceId: exact.workspaceId,
        taskPath: exact.taskPath,
        requestId: currentRequest.id,
        responder,
        response
      });
      if (wasPending) {
        ctx.events.emit(
          "decisionRequest.resolved",
          exact.workspaceId,
          projectDecisionRequest(answered),
          "self"
        );
      }
      let nextTask = task;
      ctx.host.markSelfWrite(exact.workspaceId);
      nextTask = await taskResume(mount.env, exact.taskPath);
      emitTaskState(ctx, exact.workspaceId, nextTask, "decisionRequest.respond");
      return { answered, input, task: nextTask, enqueue: true };
    })
  );
  if (result.enqueue) {
    ctx.events.emit(
      "taskInput.pending",
      exact.workspaceId,
      projectTaskInput(result.input),
      "self"
    );
    enqueueManagedTaskInputBackground(ctx, result.input);
  }
  return {
    request: projectDecisionRequest(result.answered),
    input: projectTaskInput(result.input),
    task: projectTask(result.task),
    state: result.task.state,
    accepted: true,
    enqueued: result.enqueue
  };
}
function coreDecisionRequest(item) {
  return item.status === "pending" ? {
    id: item.id,
    taskId: item.taskId,
    requester: item.requester,
    target: item.target,
    question: item.question,
    options: item.options,
    status: "pending"
  } : {
    id: item.id,
    taskId: item.taskId,
    requester: item.requester,
    target: item.target,
    question: item.question,
    options: item.options,
    status: "answered",
    response: item.response,
    resolvedBy: item.resolvedBy
  };
}
async function decisionRequestEscalateRpc(ctx, p, caller) {
  assertAllowedParams(
    p,
    /* @__PURE__ */ new Set(["workspaceId", "taskPath", "requestId"]),
    "decisionRequest.escalate"
  );
  const exact = await requireExactDecisionRequest(ctx, p);
  const responder = await decisionCallerAuthority(ctx, exact.workspaceId, caller);
  assertDecisionAuthority(exact.request, responder);
  if (responder.kind !== "role") {
    throw new RpcError(-32001, "Only the frozen Role target may escalate to user");
  }
  const mount = ctx.host.require(exact.workspaceId);
  const escalated = await runTaskLifecycle(
    exact.workspaceId,
    exact.taskPath,
    () => ctx.mutations.run(exact.workspaceId, async () => {
      const current = await ctx.decisionRequests.getExact(
        exact.workspaceId,
        exact.taskPath,
        exact.request.id
      );
      if (!current) {
        throw new RpcError(-32004, `Decision Request not found: ${exact.request.id}`);
      }
      assertDecisionAuthority(current, responder);
      if (current.status !== "pending") {
        throw new RpcError(RPC_LIFECYCLE, `Decision Request is already answered: ${current.id}`);
      }
      const task = await loadTaskRecord(mount.env.fs, exact.taskPath);
      if (!task.id || task.id !== current.taskId || TERMINAL_TASK_STATES.has(task.state)) {
        throw new RpcError(RPC_LIFECYCLE, "Cannot escalate a Decision Request for a terminal or changed Task", {
          code: "DECISION_TASK_TERMINAL_OR_CHANGED",
          requestId: current.id,
          taskId: current.taskId,
          state: task.state
        });
      }
      return ctx.decisionRequests.escalateExact(
        exact.workspaceId,
        exact.taskPath,
        exact.request.id
      );
    })
  );
  ctx.events.emit(
    "decisionRequest.pending",
    exact.workspaceId,
    projectDecisionRequest(escalated),
    "self"
  );
  return { request: projectDecisionRequest(escalated) };
}
function projectDecisionRequest(item) {
  return {
    id: item.id,
    workspaceId: item.workspaceId,
    taskPath: item.taskPath,
    taskId: item.taskId,
    requester: item.requester,
    target: item.target,
    question: item.question,
    options: item.options,
    status: item.status,
    ...item.status === "answered" ? { response: item.response, resolvedBy: item.resolvedBy, answeredAt: item.answeredAt } : {},
    createdAt: item.createdAt,
    updatedAt: item.updatedAt
  };
}
function requireTaskInputScope(p) {
  const workspaceId = optionalString2(p, "workspaceId");
  if (!workspaceId) {
    throw new RpcError(
      -32602,
      "taskInput.* requires explicit workspaceId (no global inbox / no foreground fallback)"
    );
  }
  const taskPath = requireString(p, "taskPath");
  return { workspaceId, taskPath };
}
async function taskInputListPending(ctx, p) {
  const { workspaceId, taskPath } = requireTaskInputScope(p);
  ctx.host.require(workspaceId);
  try {
    const attention2 = await ctx.taskInputs.listAttentionForTask(
      workspaceId,
      taskPath
    );
    const visible = await filterPublishedTaskInputs(ctx, attention2);
    return { inputs: visible.map(projectTaskInput) };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(-32602, message2);
  }
}
async function taskInputGet(ctx, p) {
  const { workspaceId, taskPath } = requireTaskInputScope(p);
  const inputId = requireString(p, "inputId");
  ctx.host.require(workspaceId);
  let item;
  try {
    item = await ctx.taskInputs.get(inputId, workspaceId, taskPath);
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(-32602, message2);
  }
  if (!item) throw new RpcError(-32004, `TaskInput not found: ${inputId}`);
  if (await isUnpublishedDecisionResponseInput(ctx, item)) {
    throw new RpcError(-32004, `TaskInput not found: ${inputId}`);
  }
  return { input: projectTaskInput(item) };
}
async function taskInputAckRpc(ctx, p) {
  const { workspaceId, taskPath } = requireTaskInputScope(p);
  const inputId = requireString(p, "inputId");
  const actorRaw = optionalString2(p, "actor")?.trim();
  ctx.host.require(workspaceId);
  let existing;
  try {
    existing = await ctx.taskInputs.get(inputId, workspaceId, taskPath);
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new RpcError(-32602, message2);
  }
  if (!existing) throw new RpcError(-32004, `TaskInput not found: ${inputId}`);
  if (await isUnpublishedDecisionResponseInput(ctx, existing)) {
    throw new RpcError(
      RPC_LIFECYCLE,
      "Decision response TaskInput is not published until its Decision Request is answered",
      { inputId, workspaceId, taskPath }
    );
  }
  const authority = await resolveTaskInputAckAuthority(ctx, existing, actorRaw);
  const actor = authority.actor;
  let item;
  try {
    item = await ctx.taskInputs.ack(inputId, workspaceId, taskPath, actor);
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    if (/not found/i.test(message2)) {
      throw new RpcError(-32004, message2);
    }
    if (/already /i.test(message2)) {
      throw new RpcError(RPC_LIFECYCLE, message2, { inputId });
    }
    throw new RpcError(-32603, message2);
  }
  ctx.events.emit(
    "taskInput.consumed",
    item.workspaceId,
    {
      inputId: item.id,
      taskPath: item.taskPath,
      sessionId: item.sessionId,
      actor,
      status: item.status
    },
    "self"
  );
  if (existing.status === "uncertain") {
    const sessionId = existing.sessionId ?? authority.task.executionSessionId;
    if (sessionId) {
      trackManagedTaskInputBackground(
        requestManagedAutoSubmitRetryFromDraft(ctx, {
          workspaceId,
          taskPath,
          sessionId
        })
      );
    }
  }
  return { input: projectTaskInput(item) };
}
async function resolveTaskInputAckAuthority(ctx, item, actorRaw) {
  const mount = ctx.host.get(item.workspaceId);
  if (!mount) {
    throw new RpcError(-32e3, `Workspace not mounted: ${item.workspaceId}`);
  }
  const task = await loadTaskRecord(mount.env.fs, item.taskPath).catch(
    (error) => {
      throw new RpcError(
        -32e3,
        `taskInput.ack cannot read exact Task: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  );
  if (!actorRaw) {
    if (task.requester?.kind === "user") {
      return { actor: "user", task };
    }
    throw new RpcError(
      -32001,
      "taskInput.ack user path requires the exact persisted requester to be user",
      { inputId: item.id, workspaceId: item.workspaceId, taskPath: item.taskPath }
    );
  }
  if (actorRaw === "user") {
    throw new RpcError(
      -32001,
      'taskInput.ack rejects caller-supplied actor "user"; omit actor for the Local Service user path',
      { inputId: item.id, workspaceId: item.workspaceId, taskPath: item.taskPath }
    );
  }
  if (task.assigneeRoleId && actorRaw === task.assigneeRoleId) {
    return { actor: actorRaw, task };
  }
  if (task.requester?.kind === "role" && task.requester.id === actorRaw) {
    return { actor: actorRaw, task };
  }
  if (item.sessionId && actorRaw === item.sessionId) {
    try {
      const rec = await ctx.runtime.registry.read(item.sessionId);
      if (rec) {
        const workspaceMatches = rec.workspace === item.workspaceId;
        const taskMatches = rec.currentTaskId === task.id && task.executionSessionId === item.sessionId;
        if (workspaceMatches && taskMatches) {
          return { actor: actorRaw, task };
        }
      }
    } catch {
    }
  }
  throw new RpcError(
    -32001,
    "taskInput.ack actor must match the exact Task role, persisted parent Role, or a service-verified Session binding",
    {
      inputId: item.id,
      actor: actorRaw,
      expectedRoleId: task.assigneeRoleId,
      requester: task.requester,
      sessionId: item.sessionId,
      workspaceId: item.workspaceId,
      taskPath: item.taskPath
    }
  );
}
function projectTaskInput(item) {
  return {
    id: item.id,
    workspaceId: item.workspaceId,
    taskPath: item.taskPath,
    taskId: item.taskId,
    sessionId: item.sessionId,
    role: item.role,
    kind: normalizeTaskInputKind(item.kind),
    text: item.text,
    contextRefs: item.contextRefs,
    status: item.status,
    createdAt: item.createdAt,
    updatedAt: item.updatedAt,
    deliveredAt: item.deliveredAt,
    consumedAt: item.consumedAt,
    cancelledAt: item.cancelledAt,
    lastError: item.lastError,
    failedAt: item.failedAt,
    uncertainAt: item.uncertainAt,
    resolvedBy: item.resolvedBy
  };
}
async function isUnpublishedDecisionResponseInput(ctx, item) {
  if (normalizeTaskInputKind(item.kind) !== "decision-response") return false;
  const pending = await ctx.decisionRequests.getPendingForTask(
    item.workspaceId,
    item.taskPath
  );
  return !!pending && taskInputIdForDecisionRequest(pending.id) === item.id;
}
async function filterPublishedTaskInputs(ctx, items) {
  const visibility = await Promise.all(
    items.map(async (item) => !await isUnpublishedDecisionResponseInput(ctx, item))
  );
  return items.filter((_item, index2) => visibility[index2]);
}
async function removePendingDecisionRequestForTerminal(ctx, workspaceId, taskPath, resolvedBy) {
  let removed;
  try {
    removed = await ctx.decisionRequests.removePendingForTask(workspaceId, taskPath);
  } catch (error) {
    const pending = await ctx.decisionRequests.getPendingForTask(workspaceId, taskPath).catch(() => void 0);
    ctx.events.emit(
      "decisionRequest.resolved",
      workspaceId,
      {
        ...pending ? { requestId: pending.id, taskId: pending.taskId } : {},
        taskPath,
        terminal: true,
        resolvedBy,
        cleanupFailed: true,
        diagnostic: error instanceof Error ? error.message : String(error)
      },
      "self"
    );
    return;
  }
  if (!removed) return;
  ctx.events.emit(
    "decisionRequest.resolved",
    workspaceId,
    {
      requestId: removed.id,
      taskId: removed.taskId,
      taskPath: removed.taskPath,
      terminal: true,
      resolvedBy
    },
    "self"
  );
}
async function cancelTaskInputsForTask(ctx, workspaceId, taskPath, resolvedBy) {
  try {
    const cancelled = await ctx.taskInputs.cancelTask(
      workspaceId,
      taskPath,
      resolvedBy
    );
    for (const input of cancelled) {
      ctx.events.emit(
        "taskInput.cancelled",
        workspaceId,
        {
          inputId: input.id,
          decision: "cancelled",
          actor: resolvedBy,
          taskPath: input.taskPath,
          sessionId: input.sessionId
        },
        "service"
      );
    }
  } catch {
  }
}
async function cancelTaskInputsForSession(ctx, workspaceId, sessionId, resolvedBy) {
  try {
    const cancelled = await ctx.taskInputs.cancelSession(sessionId, resolvedBy);
    for (const input of cancelled) {
      ctx.events.emit(
        "taskInput.cancelled",
        workspaceId || input.workspaceId,
        {
          inputId: input.id,
          decision: "cancelled",
          actor: resolvedBy,
          taskPath: input.taskPath,
          sessionId: input.sessionId
        },
        "service"
      );
    }
  } catch {
  }
}
async function operationalRetentionPreviewRpc(ctx, p) {
  requireUserActor(p, "operationalRetention.preview");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const keepTerminalTasksDays = parseKeepTerminalTasksDays(p);
  const preview = await previewOperationalRetention(mount.env.fs, {
    keepTerminalTasksDays,
    now: mount.env.clock.now()
  });
  return { workspaceId, ...preview };
}
async function taskWorktreeReclaimPreviewRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const task = await loadTaskRecord(mount.env.fs, taskPath);
  const diagnostic = await evaluateTaskWorktreeReclaimForEnvelope(
    mount.env.fs,
    mount.workspaceRoot,
    task
  );
  return {
    workspaceId,
    taskPath,
    ...diagnostic
  };
}
async function taskWorktreeReclaimReconcileRpc(ctx, p, callContext) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const taskPath = requireString(p, "taskPath");
  const task = await loadTaskRecord(mount.env.fs, taskPath);
  await requireTaskWorktreeReconcileActor(ctx, workspaceId, p, task, callContext);
  const result = await reconcileExactTaskWorktree(
    ctx,
    workspaceId,
    task,
    "task.worktreeReclaim.reconcile"
  );
  const diagnostic = result ?? await evaluateTaskWorktreeReclaimForEnvelope(
    mount.env.fs,
    mount.workspaceRoot,
    task
  );
  return { workspaceId, taskPath, ...diagnostic };
}
async function requireTaskWorktreeReconcileActor(ctx, workspaceId, p, task, callContext) {
  const actor = requireString(p, "actor");
  await resolveReviewCallerActor(
    ctx,
    workspaceId,
    actor,
    "worktreeReclaim.reconcile",
    callContext
  );
  if (actor === "user") return actor;
  const authorized = task.requester?.kind === "role" && task.requester.id === actor;
  if (!authorized) {
    throw new RpcError(
      -32001,
      "task.worktreeReclaim.reconcile requires user or the exact Task parent Role",
      { actor, taskId: task.id, taskPath: task.path }
    );
  }
  return actor;
}
async function assertTaskExecutionSettledForWorktreeReclaim(ctx, workspaceId, task) {
  const sessionId = task.executionSessionId?.trim();
  if (!sessionId) {
    return {
      ok: false,
      reason: "Task has no exact executionSessionId; live execution cannot be confirmed without a global scan.",
      details: { taskId: task.id, taskPath: task.path }
    };
  }
  try {
    const rec = await ctx.runtime.registry.read(sessionId);
    if (!rec) {
      return {
        ok: false,
        reason: `Exact bound session ${sessionId} is missing; refuse worktree reclaim.`,
        details: { sessionId }
      };
    }
    if (rec.workspace !== workspaceId || !task.id || rec.currentTaskId !== task.id) {
      return {
        ok: false,
        reason: `Exact bound session ${sessionId} does not match the Task/workspace binding; refuse worktree reclaim.`,
        details: {
          sessionId,
          sessionWorkspace: rec.workspace,
          currentTaskId: rec.currentTaskId,
          taskId: task.id
        }
      };
    }
    if (rec.state === "external" || SessionRegistry.isOpen(rec.state)) {
      return {
        ok: false,
        reason: `Bound session ${sessionId} is still open (state=${rec.state}); stop or session.leave before reclaim.`,
        details: { sessionId, state: rec.state, currentTaskId: rec.currentTaskId }
      };
    }
    if (SessionRegistry.isNonTerminal(rec.state)) {
      return {
        ok: false,
        reason: `Bound session ${sessionId} is still non-terminal (state=${rec.state}); stop before reclaim.`,
        details: { sessionId, state: rec.state }
      };
    }
    const probe = await ctx.runtime.probe(sessionId);
    if (probe.isTurnActive === true) {
      return {
        ok: false,
        reason: `Bound session ${sessionId} still has isTurnActive=true; refuse worktree reclaim until the turn settles.`,
        details: { sessionId, isTurnActive: true, state: probe.state }
      };
    }
    if (probe.isAlive || SessionRegistry.isNonTerminal(probe.state) || probe.state === "external") {
      return {
        ok: false,
        reason: `Bound session ${sessionId} is still live/open (state=${probe.state}, alive=${probe.isAlive}); stop or leave before reclaim.`,
        details: { sessionId, state: probe.state, isAlive: probe.isAlive }
      };
    }
  } catch (err) {
    return {
      ok: false,
      reason: `Failed to probe bound session ${sessionId} for reclaim settle: ${err instanceof Error ? err.message : String(err)}`,
      details: { sessionId }
    };
  }
  return { ok: true };
}
var beforeTaskWorktreeReclaimRemoveForTests;
async function reconcileExactTaskWorktree(ctx, workspaceId, task, reason) {
  const mount = ctx.host.get(workspaceId);
  if (!mount) return void 0;
  if (!isTaskScopedWorktreeLane(task)) return void 0;
  if (!isTaskWorktreeReclaimTerminalState(task.state)) return void 0;
  if (!task.worktree && !task.branch) return void 0;
  const taskId = task.id?.trim();
  if (!taskId) return void 0;
  const taskPath = task.path;
  try {
    return await runTaskLifecycle(workspaceId, taskPath, async () => {
      let liveTask;
      try {
        liveTask = await loadTaskRecord(mount.env.fs, taskPath);
      } catch (error) {
        const blocked = {
          eligible: false,
          code: "REMOVE_FAILED",
          reason: `Exact Task envelope is unreadable under reclaim critical section; refuse remove: ${error instanceof Error ? error.message : String(error)}`,
          taskId,
          taskPath,
          taskState: task.state,
          workspace: task.workspace,
          worktree: task.worktree,
          branch: task.branch,
          targetBranch: task.targetBranch,
          details: { stage: "reload-task-envelope" },
          reclaimed: false,
          alreadyGone: false
        };
        ctx.events.emit(
          "task.worktreeReclaim",
          workspaceId,
          {
            taskId,
            taskPath,
            taskState: task.state,
            code: "UNREADABLE_TASK",
            eligible: false,
            reclaimed: false,
            alreadyGone: false,
            reason: blocked.reason,
            worktree: task.worktree,
            branch: task.branch,
            trigger: reason
          },
          "self"
        );
        return blocked;
      }
      if (!isTaskWorktreeReclaimTerminalState(liveTask.state)) {
        const blocked = {
          eligible: false,
          code: "NOT_TERMINAL",
          reason: `Task state=${liveTask.state} is no longer terminal under reclaim critical section; refuse remove.`,
          taskId,
          taskPath,
          taskState: liveTask.state,
          workspace: liveTask.workspace,
          worktree: liveTask.worktree,
          branch: liveTask.branch,
          targetBranch: liveTask.targetBranch,
          reclaimed: false,
          alreadyGone: false
        };
        ctx.events.emit(
          "task.worktreeReclaim",
          workspaceId,
          {
            taskId,
            taskPath,
            taskState: liveTask.state,
            code: blocked.code,
            eligible: false,
            reclaimed: false,
            alreadyGone: false,
            reason: blocked.reason,
            worktree: liveTask.worktree,
            branch: liveTask.branch,
            trigger: reason
          },
          "self"
        );
        return blocked;
      }
      const settled = await assertTaskExecutionSettledForWorktreeReclaim(
        ctx,
        workspaceId,
        liveTask
      );
      if (!settled.ok) {
        const blocked = {
          eligible: false,
          code: "SESSION_ACTIVE",
          reason: settled.reason,
          taskId,
          taskPath: liveTask.path,
          taskState: liveTask.state,
          workspace: liveTask.workspace,
          worktree: liveTask.worktree,
          branch: liveTask.branch,
          targetBranch: liveTask.targetBranch,
          details: settled.details,
          reclaimed: false,
          alreadyGone: false
        };
        ctx.events.emit(
          "task.worktreeReclaim",
          workspaceId,
          {
            taskId,
            taskPath: liveTask.path,
            taskState: liveTask.state,
            code: blocked.code,
            eligible: false,
            reclaimed: false,
            alreadyGone: false,
            reason: blocked.reason,
            worktree: liveTask.worktree,
            branch: liveTask.branch,
            trigger: reason,
            ...blocked.details ? { details: blocked.details } : {}
          },
          "self"
        );
        return blocked;
      }
      const result = await reclaimTaskWorktreeForEnvelope(
        mount.env.fs,
        mount.workspaceRoot,
        liveTask,
        {
          // Re-probe bound Session immediately before exact remove (inside same lock).
          assertSessionSettledBeforeRemove: () => assertTaskExecutionSettledForWorktreeReclaim(
            ctx,
            workspaceId,
            liveTask
          ),
          beforeRemoveForTests: beforeTaskWorktreeReclaimRemoveForTests
        }
      );
      if (result.reclaimed || !result.eligible) {
        ctx.events.emit(
          "task.worktreeReclaim",
          workspaceId,
          {
            taskId: result.taskId ?? liveTask.id,
            taskPath: result.taskPath ?? liveTask.path,
            taskState: result.taskState ?? liveTask.state,
            code: result.code,
            eligible: result.eligible,
            reclaimed: result.reclaimed,
            alreadyGone: result.alreadyGone,
            reason: result.reason,
            worktree: result.worktree,
            branch: result.branch,
            trigger: reason,
            ...result.details ? { details: result.details } : {}
          },
          "self"
        );
      }
      return result;
    });
  } catch (err) {
    const blocked = {
      eligible: false,
      code: "REMOVE_FAILED",
      reason: `Exact Task worktree reclaim threw: ${err instanceof Error ? err.message : String(err)}`,
      taskId: task.id,
      taskPath: task.path,
      taskState: task.state,
      workspace: task.workspace,
      worktree: task.worktree,
      branch: task.branch,
      targetBranch: task.targetBranch,
      reclaimed: false,
      alreadyGone: false
    };
    ctx.events.emit(
      "task.worktreeReclaim",
      workspaceId,
      {
        taskId: task.id,
        taskPath: task.path,
        taskState: task.state,
        code: "REMOVE_FAILED",
        eligible: false,
        reclaimed: false,
        alreadyGone: false,
        reason: blocked.reason,
        trigger: reason
      },
      "self"
    );
    return blocked;
  }
}
async function operationalRetentionPurgeRpc(ctx, p) {
  requireUserActor(p, "operationalRetention.purge");
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const keepTerminalTasksDays = parseKeepTerminalTasksDays(p);
  return ctx.mutations.run(workspaceId, async () => {
    ctx.host.markSelfWrite(workspaceId);
    const result = await purgeOperationalRetention(mount.env.fs, {
      keepTerminalTasksDays,
      now: mount.env.clock.now()
    });
    if (result.deletedCount > 0) {
      emitRetentionPurged(ctx, workspaceId, result);
    }
    return { workspaceId, ...result };
  });
}
function requireUserActor(p, surface) {
  const actorRaw = optionalString2(p, "actor") ?? "user";
  if (actorRaw !== "user") {
    throw new RpcError(
      -32001,
      `${surface} is user-only; non-user actor is forbidden`,
      { actor: actorRaw }
    );
  }
  return actorRaw;
}
function annotationErrorToRpc(error) {
  switch (error.code) {
    case "NOT_FOUND":
      return new RpcError(-32004, error.message, { code: error.code });
    case "RANGE":
    case "QUOTE_MISMATCH":
    case "INVALID_INPUT":
    case "INVALID_STATUS":
      return new RpcError(-32602, error.message, { code: error.code });
    default:
      return new RpcError(-32602, error.message, { code: error.code });
  }
}
function projectAnnotationWire(record, documentBody) {
  return projectAnnotation(record, documentBody);
}
async function readNodeBody(mount, node2) {
  const notePath = nodeNotePath(node2.path);
  const raw = await mount.env.fs.readFile(notePath);
  const { body } = parseFrontmatter(raw);
  return { body, raw, etag: contentEtag(raw) };
}
async function annotationListRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  const nodeId = requireString(p, "nodeId");
  const tent = await loadTent(mount.env.fs);
  const node2 = requireCanonicalNode(tent, nodeId);
  const note = await readNodeBody(mount, node2);
  const documentBody = note.body;
  const records = await listAnnotationRecords(mount.env.fs, nodeId);
  const annotations = records.map((r) => projectAnnotationWire(r, documentBody));
  return { workspaceId, nodeId, annotations };
}
async function annotationCreateRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  requireUserActor(p, "annotation.create");
  const nodeId = requireString(p, "nodeId");
  const quote = typeof p.quote === "string" ? p.quote : void 0;
  const body = typeof p.body === "string" ? p.body : void 0;
  if (quote === void 0 || quote.length === 0) {
    throw new RpcError(-32602, "annotation.create requires non-empty quote");
  }
  if (body === void 0 || body.trim().length === 0) {
    throw new RpcError(-32602, "annotation.create requires non-empty body");
  }
  const start = p.start;
  const end = p.end;
  if (typeof start !== "number" || !Number.isInteger(start)) {
    throw new RpcError(-32602, "annotation.create requires integer start");
  }
  if (typeof end !== "number" || !Number.isInteger(end)) {
    throw new RpcError(-32602, "annotation.create requires integer end");
  }
  const baseEtag = optionalString2(p, "documentEtag");
  if (!baseEtag) {
    throw new RpcError(-32602, "annotation.create requires documentEtag");
  }
  return ctx.mutations.run(workspaceId, async () => {
    const tent = await loadTent(mount.env.fs);
    const node2 = requireCanonicalNode(tent, nodeId);
    const note = await readNodeBody(mount, node2);
    if (baseEtag !== note.etag) {
      throw new RpcError(-32009, "etag conflict", {
        currentEtag: note.etag,
        baseEtag,
        path: node2.path,
        nodeId
      });
    }
    try {
      const record = await createAnnotation(mount.env.fs, {
        nodeId: node2.id,
        quote,
        start,
        end,
        documentEtag: note.etag,
        body,
        documentBody: note.body
      });
      const projection = projectAnnotationWire(record, note.body);
      ctx.events.emit(
        "annotation.changed",
        workspaceId,
        {
          action: "create",
          id: record.id,
          nodeId: record.nodeId
        },
        "self"
      );
      return { workspaceId, annotation: projection };
    } catch (error) {
      if (error instanceof AnnotationError) throw annotationErrorToRpc(error);
      throw error;
    }
  });
}
async function annotationResolveRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  requireUserActor(p, "annotation.resolve");
  const id = requireString(p, "id");
  return ctx.mutations.run(workspaceId, async () => {
    try {
      const record = await resolveAnnotation(mount.env.fs, id);
      const tent = await loadTent(mount.env.fs);
      const node2 = tent.byId.get(record.nodeId) ?? null;
      let documentBody = null;
      if (node2) {
        documentBody = (await readNodeBody(mount, node2)).body;
      }
      const projection = projectAnnotationWire(record, documentBody);
      ctx.events.emit(
        "annotation.changed",
        workspaceId,
        {
          action: "resolve",
          id: record.id,
          nodeId: record.nodeId
        },
        "self"
      );
      return { workspaceId, annotation: projection };
    } catch (error) {
      if (error instanceof AnnotationError) throw annotationErrorToRpc(error);
      throw error;
    }
  });
}
async function annotationReopenRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  requireUserActor(p, "annotation.reopen");
  const id = requireString(p, "id");
  return ctx.mutations.run(workspaceId, async () => {
    try {
      const record = await reopenAnnotation(mount.env.fs, id);
      const tent = await loadTent(mount.env.fs);
      const node2 = tent.byId.get(record.nodeId) ?? null;
      let documentBody = null;
      if (node2) {
        documentBody = (await readNodeBody(mount, node2)).body;
      }
      const projection = projectAnnotationWire(record, documentBody);
      ctx.events.emit(
        "annotation.changed",
        workspaceId,
        {
          action: "reopen",
          id: record.id,
          nodeId: record.nodeId
        },
        "self"
      );
      return { workspaceId, annotation: projection };
    } catch (error) {
      if (error instanceof AnnotationError) throw annotationErrorToRpc(error);
      throw error;
    }
  });
}
async function annotationDeleteRpc(ctx, p) {
  const workspaceId = requireWorkspaceId(ctx, p);
  const mount = ctx.host.require(workspaceId);
  requireUserActor(p, "annotation.delete");
  const id = requireString(p, "id");
  return ctx.mutations.run(workspaceId, async () => {
    try {
      const removed = await deleteAnnotation(mount.env.fs, id);
      if (!removed) {
        throw new RpcError(-32004, `Annotation not found: ${id}`);
      }
      ctx.events.emit(
        "annotation.changed",
        workspaceId,
        {
          action: "delete",
          id: removed.id,
          nodeId: removed.nodeId
        },
        "self"
      );
      return { workspaceId, deleted: true, id: removed.id, nodeId: removed.nodeId };
    } catch (error) {
      if (error instanceof AnnotationError) throw annotationErrorToRpc(error);
      throw error;
    }
  });
}
function parseKeepTerminalTasksDays(p) {
  const v = p.keepTerminalTasksDays;
  try {
    return normalizeKeepTerminalTasksDays(v);
  } catch (error) {
    if (error instanceof RetentionError || error instanceof Error && error.name === "RetentionError") {
      throw new RpcError(-32602, error.message, {
        code: error instanceof RetentionError ? error.code : "INVALID_KEEP_DAYS"
      });
    }
    throw error;
  }
}
function emitRetentionPurged(ctx, workspaceId, result) {
  ctx.events.emit(
    "retention.purged",
    workspaceId,
    {
      keepTerminalTasksDays: result.keepTerminalTasksDays,
      cutoff: result.cutoff,
      deletedCount: result.deletedCount,
      taskPaths: result.purged.taskPaths,
      resultPaths: result.purged.resultPaths,
      candidateTaskCount: result.candidateTaskCount,
      candidateTaskResultCount: result.candidateTaskResultCount,
      warnings: result.warnings
    },
    "self"
  );
}
var managedAutoSubmitFlights = /* @__PURE__ */ new Map();
var rejectResumeNativeInFlight = /* @__PURE__ */ new Set();
var managedSessionInFlight = /* @__PURE__ */ new Map();
var MANAGED_SESSION_IN_PROGRESS_MESSAGE = "managed session operation already in progress for this task";
function managedSessionFlightKey(workspaceId, taskPath) {
  return `${workspaceId}\0${taskPath}`;
}
function joinOrConflictManagedSessionFlight(existing, route, operation, taskPath) {
  if (existing.operation !== operation || existing.route.taskId !== route.taskId || existing.route.sessionId !== route.sessionId || existing.route.connectionId !== route.connectionId) {
    throw new RpcError(RPC_LIFECYCLE, MANAGED_SESSION_IN_PROGRESS_MESSAGE, {
      taskPath,
      route,
      operation,
      inFlightRoute: existing.route,
      inFlightOperation: existing.operation,
      retryable: true
    });
  }
  return existing.promise;
}
async function runManagedSessionFlight(workspaceId, taskPath, route, operation, run) {
  const flightKey = managedSessionFlightKey(workspaceId, taskPath);
  const existing = managedSessionInFlight.get(flightKey);
  if (existing) return joinOrConflictManagedSessionFlight(existing, route, operation, taskPath);
  let settle;
  let rejectFlight;
  const flightPromise = new Promise((resolve15, reject) => {
    settle = resolve15;
    rejectFlight = reject;
  });
  flightPromise.catch(() => void 0);
  managedSessionInFlight.set(flightKey, { route, operation, promise: flightPromise });
  try {
    const result = await run();
    settle(result);
    return result;
  } catch (err) {
    rejectFlight(err);
    throw err;
  } finally {
    if (managedSessionInFlight.get(flightKey)?.promise === flightPromise) {
      managedSessionInFlight.delete(flightKey);
    }
  }
}
var runtimeProjectionQueue = new MutationBus();
var runtimeProjectionTestHooks = null;
function managedSubmitKey(sessionId, taskPath) {
  return `${sessionId}::${taskPath}`;
}
async function requestManagedAutoSubmitRetryFromDraft(ctx, input) {
  const key2 = managedSubmitKey(input.sessionId, input.taskPath);
  const active = managedAutoSubmitFlights.get(key2);
  if (active) await active;
  let draft = await ctx.managedTaskResultReportDrafts.get(input.workspaceId, input.taskPath);
  if (!draft) return;
  if (draft.sessionId !== input.sessionId) return;
  let priorOutcome = parseTaskOutcomeReport(draft.assistantText);
  if (priorOutcome?.outcome === "blocked") {
    return;
  }
  const raced = managedAutoSubmitFlights.get(key2);
  if (raced) {
    await raced;
    draft = await ctx.managedTaskResultReportDrafts.get(input.workspaceId, input.taskPath);
    if (!draft || draft.sessionId !== input.sessionId) return;
    priorOutcome = parseTaskOutcomeReport(draft.assistantText);
    if (priorOutcome?.outcome === "blocked") {
      return;
    }
  }
  await tryManagedAutoSubmit(ctx, {
    ...input,
    assistantText: ""
  });
}
function isManagedAutoSubmitSealing(sessionId, taskPath, taskId) {
  if (managedAutoSubmitFlights.has(managedSubmitKey(sessionId, taskPath))) {
    return true;
  }
  if (taskId && managedAutoSubmitFlights.has(managedSubmitKey(sessionId, taskId))) {
    return true;
  }
  return false;
}
function classifyProjectionError(err) {
  if (err instanceof TaskLifecycleError) {
    return { errorClass: "TaskLifecycleError", errorCode: err.code };
  }
  if (err instanceof RpcError) {
    return { errorClass: "RpcError", errorCode: err.code };
  }
  if (err && typeof err === "object") {
    const e = err;
    const errorClass = typeof e.name === "string" && e.name || e.constructor?.name || "Error";
    const errorCode = typeof e.code === "string" || typeof e.code === "number" ? e.code : void 0;
    return errorCode !== void 0 ? { errorClass, errorCode } : { errorClass };
  }
  return { errorClass: "UnknownError" };
}
function mapRuntimeEventToService(ctx, ev) {
  return runtimeProjectionQueue.run(ev.sessionId, async () => {
    try {
      await projectRuntimeEvent(ctx, ev);
    } catch (err) {
      await reportRuntimeProjectionFailure(ctx, ev, err);
    }
  });
}
async function reportRuntimeProjectionFailure(ctx, ev, err) {
  const classified = classifyProjectionError(err);
  let workspaceId = "";
  try {
    const rec = await ctx.runtime.registry.read(ev.sessionId);
    workspaceId = rec?.workspace ?? ctx.host.getForegroundId() ?? "";
  } catch {
    workspaceId = ctx.host.getForegroundId() ?? "";
  }
  console.error(
    `[tent-service] runtime projection failed sessionId=${ev.sessionId} event=${ev.type} class=${classified.errorClass}` + (classified.errorCode !== void 0 ? ` code=${classified.errorCode}` : "")
  );
  ctx.events.emit(
    "service.health",
    workspaceId,
    {
      action: "runtime-projection-failed",
      sessionId: ev.sessionId,
      runtimeEvent: ev.type,
      errorClass: classified.errorClass,
      ...classified.errorCode !== void 0 ? { errorCode: classified.errorCode } : {}
    },
    "service"
  );
}
async function projectRuntimeEvent(ctx, ev) {
  if (runtimeProjectionTestHooks?.beforeProject) {
    await runtimeProjectionTestHooks.beforeProject(ev);
  }
  if (runtimeProjectionTestHooks && typeof runtimeProjectionTestHooks.failProjectionsRemaining === "number" && runtimeProjectionTestHooks.failProjectionsRemaining > 0) {
    runtimeProjectionTestHooks.failProjectionsRemaining -= 1;
    const injected = new Error("injected runtime projection failure");
    injected.name = "ProjectionInjectedError";
    injected.code = "PROJECTION_INJECTED";
    throw injected;
  }
  const rec = await ctx.runtime.registry.read(ev.sessionId);
  if (ev.type === "session.prompt_complete" && !rec?.currentTaskId) {
    throw new Error(
      `Managed prompt completion has no task binding: ${ev.sessionId}`
    );
  }
  const workspaceId = rec?.workspace ?? ctx.host.getForegroundId() ?? "";
  if (ev.type === "session.stdout_tail") {
    return;
  }
  if (ev.type === "session.config_options") {
    return;
  }
  if (ev.type === "session.acp_observation") {
    return;
  }
  const hasPendingToolApproval = ev.type === "session.live" ? await ctx.toolApprovals.hasPendingForSession(ev.sessionId) : false;
  if (ev.type === "session.waiting_user") {
    if (rec && SessionRegistry.isNonTerminal(rec.state)) {
      await ctx.runtime.registry.update(ev.sessionId, {
        state: "waiting-user"
      });
    }
  } else if (ev.type === "session.live") {
    const current = await ctx.runtime.registry.read(ev.sessionId);
    if (current && SessionRegistry.isNonTerminal(current.state) && hasPendingToolApproval) {
      await ctx.runtime.registry.update(ev.sessionId, {
        state: "waiting-user"
      });
    } else if (current && current.state === "waiting-user") {
      await ctx.runtime.registry.update(ev.sessionId, {
        state: "live",
        ...ev.pid != null ? { pid: ev.pid } : {}
      });
    }
  } else if (ev.type === "session.failed" || ev.type === "session.exited") {
    await ctx.toolApprovals.cancelSession(ev.sessionId, "denied");
    const boundTaskForTerminal = await loadBoundTaskForSessionTerminal(
      ctx,
      rec,
      ev.sessionId
    );
    const retainInputsOnTerminal = shouldRetainInputsOnSessionTerminal({
      sessionId: ev.sessionId,
      stopReason: rec?.stopReason,
      task: boundTaskForTerminal
    });
    const boundPreTaskResultActive = !!boundTaskForTerminal && (boundTaskForTerminal.state === "running" || boundTaskForTerminal.state === "waiting") && !isTaskCollaborationTerminal(boundTaskForTerminal);
    if (!retainInputsOnTerminal && !boundTaskForTerminal) {
      await cancelTaskInputsForSession(
        ctx,
        workspaceId,
        ev.sessionId,
        ev.type === "session.failed" ? "session.failed" : "session.exited"
      );
    } else if (!retainInputsOnTerminal && boundTaskForTerminal && !boundPreTaskResultActive && boundTaskForTerminal.state === "failed") {
      await cancelTaskInputsForTask(
        ctx,
        workspaceId,
        boundTaskForTerminal.path,
        ev.type === "session.failed" ? "session.failed" : "session.exited"
      );
    }
  }
  if (rec?.currentTaskId) {
    const mountInfos = ctx.host.list();
    for (const info of mountInfos) {
      if (rec.workspace && info.workspaceId !== rec.workspace) continue;
      const mount = ctx.host.get(info.workspaceId);
      if (!mount) continue;
      const tasks = await loadTaskRecords(mount.env.fs);
      const currentTask = tasks.find((t) => {
        if (t.id !== rec.currentTaskId && t.path !== rec.currentTaskId) return false;
        return t.executionSessionId === ev.sessionId;
      });
      const task = currentTask;
      if (!task) continue;
      if (ev.type === "session.waiting_user" && task.state === "running") {
        await ctx.mutations.run(mount.workspaceId, async () => {
          ctx.host.markSelfWrite(mount.workspaceId);
          const waited = await taskWait(mount.env, task.path, {
            reason: "user-input",
            summary: ev.summary
          });
          emitTaskState(ctx, mount.workspaceId, waited, "session.waiting_user");
        });
      } else if (ev.type === "session.live" && !hasPendingToolApproval && task.state === "waiting" && task.wait?.reason === "user-input") {
        const pendingDecision = await ctx.decisionRequests.getPendingForTask(
          mount.workspaceId,
          task.path
        );
        if (!pendingDecision) {
          await ctx.mutations.run(mount.workspaceId, async () => {
            ctx.host.markSelfWrite(mount.workspaceId);
            const resumed = await taskResume(mount.env, task.path);
            emitTaskState(ctx, mount.workspaceId, resumed, "session.live");
          });
        }
      } else if ((ev.type === "session.failed" || ev.type === "session.exited") && (task.state === "running" || task.state === "waiting")) {
        if (shouldSkipTaskFailOnSessionTerminal({
          sessionId: ev.sessionId,
          eventType: ev.type,
          stopReason: rec?.stopReason,
          task
        })) {
          continue;
        }
        await parkTaskForUnavailableSession(ctx, {
          workspaceId: mount.workspaceId,
          taskPath: task.path,
          sessionId: ev.sessionId,
          reason: ev.type,
          detail: ev.type === "session.failed" ? ev.error : `Managed session exited before result (code=${ev.exitCode ?? "unknown"})`
        });
      } else if (ev.type === "session.prompt_complete") {
        await tryManagedAutoSubmit(ctx, {
          workspaceId: mount.workspaceId,
          taskPath: task.path,
          sessionId: ev.sessionId,
          assistantText: ev.assistantText
        });
      }
    }
  }
  ctx.events.emit(
    "session.state",
    workspaceId,
    {
      sessionId: ev.sessionId,
      runtimeEvent: ev.type,
      ..."pid" in ev ? { pid: ev.pid } : {},
      ..."exitCode" in ev ? { exitCode: ev.exitCode } : {},
      ..."error" in ev ? { error: ev.error } : {},
      ..."summary" in ev ? { summary: ev.summary } : {},
      ...ev.type === "session.prompt_complete" ? { assistantChars: ev.assistantText.length, stopReason: ev.stopReason } : {}
    },
    "service"
  );
}
function isRejectResumeParkedWait(task) {
  return task.state === "waiting" && task.wait?.reason === "external" && typeof task.wait.summary === "string" && (task.wait.summary.includes(REJECT_RESUME_SESSION_FAILED_WAIT_SUMMARY) || task.wait.summary.includes(EXTERNAL_ROLE_REJECT_RESUME_FAILED_WAIT_SUMMARY));
}
function isTaskCollaborationTerminal(task) {
  return task.state === "submitted" || task.state === "accepted" || task.state === "rejected" || task.state === "interrupted" || task.state === "failed";
}
async function loadBoundTaskForSessionTerminal(ctx, rec, sessionId) {
  if (!rec?.currentTaskId || !rec.workspace) return void 0;
  try {
    const mountInfos = ctx.host.list();
    for (const info of mountInfos) {
      if (info.workspaceId !== rec.workspace) continue;
      const mount = ctx.host.get(info.workspaceId);
      if (!mount) continue;
      const tasks = await loadTaskRecords(mount.env.fs);
      const currentTask = tasks.find((t) => {
        if (t.id !== rec.currentTaskId && t.path !== rec.currentTaskId) return false;
        return t.executionSessionId === sessionId;
      });
      return currentTask;
    }
  } catch {
  }
  return void 0;
}
function shouldRetainInputsOnSessionTerminal(input) {
  if (input.stopReason === "user") return true;
  if (rejectResumeNativeInFlight.has(input.sessionId)) return true;
  if (!input.task) return false;
  if (isRejectResumeParkedWait(input.task)) return true;
  if (isSessionUnavailableParkedWait(input.task)) return true;
  if (isTaskCollaborationTerminal(input.task) && input.task.state !== "failed") {
    return true;
  }
  return false;
}
function shouldSkipTaskFailOnSessionTerminal(input) {
  if (input.stopReason === "user") return true;
  if (input.eventType === "session.exited" && isManagedAutoSubmitSealing(input.sessionId, input.task.path, input.task.id)) {
    return true;
  }
  if (input.eventType === "session.failed" && isManagedAutoSubmitSealing(input.sessionId, input.task.path, input.task.id)) {
    return true;
  }
  if (rejectResumeNativeInFlight.has(input.sessionId)) return true;
  if (isRejectResumeParkedWait(input.task)) return true;
  if (isSessionUnavailableParkedWait(input.task)) return true;
  if (isTaskCollaborationTerminal(input.task) && input.task.state !== "failed") {
    return true;
  }
  return false;
}
async function tryManagedAutoSubmit(ctx, input) {
  const key2 = managedSubmitKey(input.sessionId.trim(), input.taskPath);
  const existing = managedAutoSubmitFlights.get(key2);
  if (existing) {
    await existing;
    return;
  }
  let owner;
  owner = tryManagedAutoSubmitOwner(ctx, input).finally(() => {
    if (managedAutoSubmitFlights.get(key2) === owner) {
      managedAutoSubmitFlights.delete(key2);
    }
  });
  managedAutoSubmitFlights.set(key2, owner);
  await owner;
}
async function tryManagedAutoSubmitOwner(ctx, input) {
  let rawReport = input.assistantText.trim();
  let sessionId = input.sessionId.trim();
  let draftLookupError;
  if (!rawReport) {
    try {
      const draft = await ctx.managedTaskResultReportDrafts.get(
        input.workspaceId,
        input.taskPath
      );
      if (draft?.assistantText?.trim()) {
        rawReport = draft.assistantText.trim();
        if (!sessionId && draft.sessionId) {
          sessionId = draft.sessionId;
        }
      }
    } catch (err) {
      draftLookupError = err;
    }
  }
  if ((!rawReport || !sessionId) && !draftLookupError) {
    return;
  }
  const parsedOutcome = parseTaskOutcomeReport(rawReport);
  const report = (parsedOutcome?.report ?? rawReport).trim();
  const draftText = rawReport;
  let draftPreserved = false;
  try {
    if (draftLookupError) throw draftLookupError;
    const mount = ctx.host.get(input.workspaceId);
    if (!mount) return;
    const pre = await loadTaskRecord(mount.env.fs, input.taskPath).catch(() => null);
    if (!pre || !["running", "submitted", "accepted"].includes(pre.state)) {
      return;
    }
    if (pre.executionSessionId !== sessionId) {
      return;
    }
    const existingDraft = await ctx.managedTaskResultReportDrafts.get(
      input.workspaceId,
      input.taskPath
    );
    if (existingDraft) {
      const exactExisting = existingDraft.sessionId === sessionId && existingDraft.assistantText.trim() === draftText;
      if (!exactExisting) {
        const priorOutcome = parseTaskOutcomeReport(existingDraft.assistantText);
        const priorWasControl = priorOutcome?.outcome === "blocked";
        if (pre.state !== "running" || !priorWasControl) {
          throw new Error(
            "managed TaskResult report draft conflicts with this Session or completion report"
          );
        }
        await ctx.managedTaskResultReportDrafts.preserve({
          workspaceId: input.workspaceId,
          taskPath: input.taskPath,
          taskId: pre.id,
          sessionId,
          assistantText: draftText
        });
      } else {
        draftPreserved = true;
      }
      draftPreserved = true;
    } else {
      await ctx.managedTaskResultReportDrafts.preserve({
        workspaceId: input.workspaceId,
        taskPath: input.taskPath,
        taskId: pre.id,
        sessionId,
        assistantText: draftText
      });
      draftPreserved = true;
    }
    if (parsedOutcome?.outcome === "blocked") {
      await handleManagedNonSubmittedOutcome(ctx, {
        workspaceId: input.workspaceId,
        taskPath: input.taskPath,
        sessionId,
        outcome: parsedOutcome.outcome,
        report: parsedOutcome.report || rawReport
      });
      return;
    }
    let published = false;
    let recoveredPublication = false;
    await runTaskLifecycle(input.workspaceId, input.taskPath, async () => {
      const recovered = await ctx.mutations.run(
        input.workspaceId,
        () => recoverCommittedTaskResult(mount.env, input.taskPath, {
          report
        })
      );
      let phase;
      if (recovered) {
        if (recovered.task.executionSessionId !== sessionId) return;
        recoveredPublication = true;
        if (recovered.prepared.kind === "done") {
          phase = { kind: "done", result: recovered.prepared.result };
        } else {
          phase = {
            kind: "auto",
            resultId: recovered.prepared.resultId,
            commits: recovered.prepared.commits,
            ...recovered.prepared.targetHead ? { targetHead: recovered.prepared.targetHead } : {},
            opts: recovered.options
          };
        }
      } else {
        const current = await loadTaskRecord(mount.env.fs, input.taskPath);
        if (current.executionSessionId !== sessionId || current.state !== "running") return;
        if (input.commits === void 0) {
          await ensureTaskWorkspaceLane(ctx, input.workspaceId, current);
        }
        await assertNoBlockingTaskInputsForSubmit(ctx, input.workspaceId, current);
        const sealed = await sealManagedSessionBeforeTaskResult(ctx, {
          workspaceId: input.workspaceId,
          sessionId,
          taskPath: input.taskPath
        });
        if (!sealed) {
          throw new Error(
            "managed session could not be sealed before auto-submit (process still mutable)"
          );
        }
        phase = await ctx.mutations.run(input.workspaceId, async () => {
          const task = await loadTaskRecord(mount.env.fs, input.taskPath);
          if (task.state !== "running") {
            return { kind: "skip" };
          }
          if (task.executionSessionId && task.executionSessionId !== sessionId) {
            return { kind: "skip" };
          }
          await assertNoBlockingTaskInputsForSubmit(ctx, input.workspaceId, task);
          await assertTaskWorktreeCleanForSubmit(mount.workspaceRoot, task, mount.env.fs);
          await assertOrdinaryExecutorLaneHistoryForSubmit(mount.workspaceRoot, task);
          let commits = input.commits;
          if (commits === void 0) {
            commits = await collectManagedTaskResultCommits(mount.workspaceRoot, task, mount.env.fs);
          }
          const pendingCommits = await resolveSubmitCommitsForExecutorLane(
            mount.workspaceRoot,
            task,
            commits
          );
          const targetHead = pendingCommits.length > 0 ? await snapshotIntegrationTargetHead(mount.workspaceRoot, task, mount.env.fs) : void 0;
          if (targetHead && afterTargetHeadSnapshotForTests) {
            await afterTargetHeadSnapshotForTests(mount.workspaceRoot);
          }
          ctx.host.markSelfWrite(input.workspaceId);
          const mode = task.acceptMode;
          const decision = mode === "agent-decide" ? "request-review" : void 0;
          const opts = {
            report,
            decision,
            ...pendingCommits.length > 0 ? { commits: pendingCommits } : {},
            ...targetHead ? { targetHead } : {}
          };
          const prepared = await prepareTaskSubmit(mount.env, input.taskPath, opts);
          if (prepared.kind === "done") {
            return { kind: "done", result: prepared.result };
          }
          return {
            kind: "auto",
            resultId: prepared.resultId,
            commits: prepared.commits,
            ...prepared.targetHead ? { targetHead: prepared.targetHead } : {},
            opts
          };
        });
      }
      if (phase.kind === "skip") return;
      let result;
      if (phase.kind === "done") {
        result = phase.result;
      } else {
        if (phase.commits.length > 0) {
          const taskForIntegrate = await loadTaskRecord(mount.env.fs, input.taskPath);
          await makeCommitIntegrator(ctx, mount.workspaceRoot, taskForIntegrate, {
            expectedTargetHead: phase.targetHead,
            action: "task.submit",
            taskPath: input.taskPath
          })(phase.commits);
        }
        result = await ctx.mutations.run(input.workspaceId, async () => {
          ctx.host.markSelfWrite(input.workspaceId);
          return finalizeTaskSubmitAuto(mount.env, input.taskPath, phase.opts, {
            kind: "auto",
            resultId: phase.resultId,
            commits: phase.commits,
            ...phase.targetHead ? { targetHead: phase.targetHead } : {}
          });
        });
      }
      published = true;
      emitTaskState(ctx, input.workspaceId, result.task, "session.prompt_complete");
      ctx.events.emit(
        "taskResult.updated",
        input.workspaceId,
        {
          id: result.result.id,
          taskId: result.result.taskId,
          status: result.result.status,
          reason: "session.prompt_complete",
          managedAuto: true
        },
        "self"
      );
    });
    if (published) {
      try {
        await ctx.managedTaskResultReportDrafts.clear(input.workspaceId, input.taskPath);
      } catch {
      }
    }
    if (published && !recoveredPublication) {
      await stopManagedSessionAfterTaskResult(ctx, {
        workspaceId: input.workspaceId,
        sessionId,
        taskPath: input.taskPath
      });
    }
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    const errorCode = err instanceof RpcError && err.data && typeof err.data === "object" && typeof err.data.code === "string" ? err.data.code : err instanceof TaskLifecycleError ? err.code : void 0;
    if (draftPreserved) {
      try {
        await ctx.managedTaskResultReportDrafts.markFailed(
          input.workspaceId,
          input.taskPath,
          message2
        );
      } catch {
      }
    }
    let taskWithReturn;
    if (draftPreserved) {
      try {
        const returnMount = ctx.host.get(input.workspaceId);
        if (!returnMount) throw new Error("workspace unmounted before managed return record");
        taskWithReturn = await runTaskLifecycle(
          input.workspaceId,
          input.taskPath,
          () => ctx.mutations.run(input.workspaceId, async () => {
            ctx.host.markSelfWrite(input.workspaceId);
            const visibleReport = report || rawReport;
            const reportFits = Buffer.byteLength(visibleReport, "utf8") <= TASK_STATUS_DETAIL_REPORT_MAX_BYTES;
            return taskRecordFailedReturn(returnMount.env, input.taskPath, {
              ...reportFits ? { report: visibleReport } : {},
              error: boundedTaskReturnError(
                message2,
                "Managed TaskResult publication failed."
              ),
              code: stableTaskReturnCode(errorCode, "MANAGED_RESULT_SUBMIT_FAILED"),
              executionSessionId: sessionId
            });
          })
        );
      } catch {
      }
    }
    if (taskWithReturn?.statusDetail?.kind === "failed") {
      emitTaskState(
        ctx,
        input.workspaceId,
        taskWithReturn,
        "session.prompt_complete.failed"
      );
    }
    try {
      const mount = ctx.host.get(input.workspaceId);
      if (!mount) return;
      const task = taskWithReturn ?? await loadTaskRecord(mount.env.fs, input.taskPath);
      if (task.state === "running" || task.state === "waiting" || task.state === "submitted") {
        try {
          await ctx.runtime.registry.update(sessionId, {
            lastError: `managed auto-submit failed: ${message2}`
          });
        } catch {
        }
        ctx.events.emit(
          "session.state",
          input.workspaceId,
          {
            sessionId,
            taskPath: input.taskPath,
            taskState: task.state,
            runtimeEvent: "session.prompt_complete.failed",
            error: message2,
            ...errorCode ? { errorCode } : {},
            // Explicit: task remains non-terminal for retry.
            taskFailed: false,
            reportDraftPreserved: draftPreserved
          },
          "service"
        );
      }
    } catch {
    }
  }
}
async function handleManagedNonSubmittedOutcome(ctx, input) {
  const mount = ctx.host.get(input.workspaceId);
  if (!mount) return;
  const sessionId = input.sessionId.trim();
  try {
    const task = await loadTaskRecord(mount.env.fs, input.taskPath).catch(() => null);
    if (!task) return;
    if (task.state !== "running" && task.state !== "waiting") return;
    if (task.executionSessionId && task.executionSessionId !== sessionId) return;
    const outcome = input.outcome;
    const report = input.report.trim() || `outcome=${outcome}`;
    if (outcome === "blocked") {
      let parked = false;
      let taskWithReturn;
      try {
        await runTaskLifecycle(input.workspaceId, input.taskPath, async () => {
          await ctx.mutations.run(input.workspaceId, async () => {
            ctx.host.markSelfWrite(input.workspaceId);
            const current = await loadTaskRecord(mount.env.fs, input.taskPath);
            if (current.state !== "running" || !task.id || current.id !== task.id || current.executionSessionId !== sessionId) {
              return;
            }
            taskWithReturn = await taskWait(mount.env, input.taskPath, {
              reason: "external",
              summary: report.slice(0, 2e3),
              code: "blocked",
              statusDetail: {
                kind: "blocked",
                report,
                at: mount.env.clock.now(),
                executionSessionId: sessionId
              }
            });
            parked = true;
          });
        });
      } catch (error) {
        const detail = error instanceof Error ? error.message : String(error);
        try {
          await runTaskLifecycle(
            input.workspaceId,
            input.taskPath,
            () => ctx.mutations.run(input.workspaceId, async () => {
              ctx.host.markSelfWrite(input.workspaceId);
              const recorded = await taskRecordFailedReturn(mount.env, input.taskPath, {
                error: boundedTaskReturnError(
                  `Managed ${outcome} return could not be recorded: ${detail}`,
                  "Managed control return could not be recorded."
                ),
                code: "MANAGED_RETURN_INVALID",
                executionSessionId: sessionId
              });
              if (recorded.statusDetail?.kind === "failed") taskWithReturn = recorded;
            })
          );
        } catch {
        }
      }
      if (parked) {
        try {
          await ctx.managedTaskResultReportDrafts.clear(input.workspaceId, input.taskPath);
        } catch {
        }
      }
      if (taskWithReturn) {
        emitTaskState(ctx, input.workspaceId, taskWithReturn, "session.prompt_complete");
      }
    }
    try {
      await ctx.runtime.registry.update(sessionId, {
        lastError: `managed outcome=${outcome} (no ready TaskResult)`
      });
    } catch {
    }
    ctx.events.emit(
      "session.state",
      input.workspaceId,
      {
        sessionId,
        taskPath: input.taskPath,
        taskState: (await loadTaskRecord(mount.env.fs, input.taskPath).catch(() => null))?.state,
        runtimeEvent: "session.prompt_complete.outcome",
        outcome,
        error: report.slice(0, 500),
        resultPublished: false,
        taskFailed: false
      },
      "service"
    );
  } catch (err) {
    console.error(
      `[managed outcome] blocked-return handling failed: ${err instanceof Error ? err.message : String(err)}`
    );
  }
}
function boundedTaskReturnError(value, fallback) {
  const text3 = value.trim() || fallback;
  if (Buffer.byteLength(text3, "utf8") <= TASK_STATUS_DETAIL_ERROR_MAX_BYTES) return text3;
  const suffix = "\u2026";
  const budget = TASK_STATUS_DETAIL_ERROR_MAX_BYTES - Buffer.byteLength(suffix, "utf8");
  const bytes = Buffer.from(text3, "utf8");
  let end = Math.min(budget, bytes.length);
  while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
  return `${bytes.subarray(0, end).toString("utf8").trimEnd()}${suffix}`;
}
function stableTaskReturnCode(value, fallback) {
  const code = value?.trim() || "";
  return code && Buffer.byteLength(code, "utf8") <= 128 && /^[A-Za-z0-9_.:-]+$/.test(code) ? code : fallback;
}
async function collectManagedTaskResultCommits(workspaceRoot, task, fs21) {
  const hasRecordedLane = Boolean(
    task.workspace || task.worktree || task.branch || task.targetBranch
  );
  if (!hasRecordedLane) {
    return [];
  }
  const base = task.roleBranchBase?.trim();
  if (!base) {
    throw new Error(
      `Managed result collection requires roleBranchBase on task ${task.id || task.path}; baseline must be captured at first Git lane bind (never fall back to all role commits).`
    );
  }
  const contract = await resolveIntegrationContract(workspaceRoot, task, fs21);
  const pending = await listPendingRoleCommits(contract, base);
  return pending.map((commit) => commit.ref);
}
async function sealManagedSessionBeforeTaskResult(ctx, input) {
  try {
    await ctx.toolApprovals.cancelSession(input.sessionId, "denied");
  } catch {
  }
  const failures = [];
  try {
    const before = await ctx.runtime.probe(input.sessionId);
    if (!before.isAlive && !before.isTurnActive) return true;
  } catch (err) {
    const message3 = err instanceof Error ? err.message : String(err);
    failures.push(`pre-stop probe failed: ${message3}`);
  }
  try {
    await ctx.runtime.stopSession(input.sessionId, "user");
  } catch (err) {
    const message3 = err instanceof Error ? err.message : String(err);
    failures.push(`stop failed: ${message3}`);
  }
  try {
    const after = await ctx.runtime.probe(input.sessionId);
    if (!after.isAlive && !after.isTurnActive) return true;
    failures.push(
      `post-stop probe remained mutable: alive=${String(after.isAlive)} isTurnActive=${String(after.isTurnActive)}`
    );
  } catch (err) {
    const message3 = err instanceof Error ? err.message : String(err);
    failures.push(`post-stop probe failed: ${message3}`);
  }
  const message2 = failures.join("; ") || "dead and idle state was not proven";
  try {
    await ctx.runtime.registry.update(input.sessionId, {
      lastError: `managed Session seal before submit failed: ${message2}`
    });
  } catch {
  }
  ctx.events.emit(
    "session.state",
    input.workspaceId,
    {
      sessionId: input.sessionId,
      taskPath: input.taskPath,
      runtimeEvent: "session.seal_before_submit.failed",
      error: message2,
      taskFailed: false
    },
    "service"
  );
  return false;
}
async function stopManagedSessionAfterTaskResult(ctx, input) {
  try {
    try {
      await ctx.toolApprovals.cancelSession(input.sessionId, "denied");
    } catch {
    }
    try {
      await cancelTaskInputsForSession(
        ctx,
        input.workspaceId,
        input.sessionId,
        "session.stop_after_submit"
      );
    } catch {
    }
    const probe = await ctx.runtime.probe(input.sessionId);
    if (probe.isAlive || SessionRegistry.isNonTerminal(probe.state)) {
      await ctx.runtime.stopSession(input.sessionId, "user");
    }
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    try {
      await ctx.runtime.registry.update(input.sessionId, {
        lastError: `managed Session stop after submit failed: ${message2}`
      });
    } catch {
    }
    ctx.events.emit(
      "session.state",
      input.workspaceId,
      {
        sessionId: input.sessionId,
        taskPath: input.taskPath,
        runtimeEvent: "session.stop_after_submit.failed",
        error: message2,
        // TaskResult already succeeded; task must not be failed for stop issues.
        taskFailed: false
      },
      "service"
    );
  }
}
var afterTargetHeadSnapshotForTests = null;
var beforeTaskSubmitFinalizeForTests = null;
var beforeTaskAcceptFinalizeForTests = null;
var afterManagedSessionProviderStartForTests = null;
var beforeTaskClaimCoreForTests = null;
var beforeReplaceTaskInputRollbackForTests = null;
var REJECT_RESUME_SESSION_FAILED_WAIT_SUMMARY = "\u9A73\u56DE\u7EED\u8DD1\u672A\u80FD\u6062\u590D\u539F managed Session\u3002\u53EF\u663E\u5F0F replaceSession\uFF0C\u6216 interrupt \u4EFB\u52A1\uFF1B\u4EFB\u52A1\u4FDD\u6301\u6D3B\u52A8\u3002";
var EXTERNAL_ROLE_REJECT_RESUME_FAILED_WAIT_SUMMARY = "\u9A73\u56DE\u7EED\u8DD1\u672A\u80FD\u6062\u590D\u539F external Role Session\u3002\u8BF7\u6062\u590D\u540C\u4E00 Session \u540E claim\uFF0C\u6216 interrupt \u4EFB\u52A1\uFF1B\u4EFB\u52A1\u4FDD\u6301\u6D3B\u52A8\u3002";
async function restoreManagedSessionAfterRejectResume(ctx, input) {
  const mount = ctx.host.require(input.workspaceId);
  let task = await loadTaskRecord(mount.env.fs, input.taskPath);
  if (task.state !== "running") {
    throw new Error(
      `reject-resume restore requires task state running; got ${task.state}`
    );
  }
  const priorSessionId = task.executionSessionId?.trim() || "";
  if (!priorSessionId) {
    throw new Error("reject-resume restore requires task.executionSessionId");
  }
  task = await ensureTaskWorkspaceLane(ctx, input.workspaceId, task);
  const cwd = task.worktree || mount.workspaceRoot;
  const prior = await ctx.runtime.registry.read(priorSessionId);
  if (!prior) {
    throw new Error(
      `Managed session registry row missing for ${priorSessionId}; cannot restore after reject-resume`
    );
  }
  const priorTaskRef = prior.currentTaskId?.trim() || "";
  if (!priorTaskRef || priorTaskRef !== task.id) {
    throw new Error(
      `Managed Session ${priorSessionId} is not bound to the exact rejected Task (session.currentTaskId=${priorTaskRef || "(missing)"}, taskId=${task.id})`
    );
  }
  const connectionId = prior.connectionId?.trim();
  const adapterId = prior.adapterId?.trim();
  if (!connectionId || !adapterId) {
    throw new Error(
      `Managed session ${priorSessionId} has no Agent Connection binding for reject-resume restore`
    );
  }
  const emptyBootstrap = "";
  let probe;
  try {
    probe = await ctx.runtime.probe(priorSessionId);
    if (probe.isAlive && SessionRegistry.isNonTerminal(probe.state)) {
      await ctx.runtime.registry.update(priorSessionId, {
        providerContextRestored: true
      });
      const bound = await ctx.mutations.run(input.workspaceId, async () => {
        ctx.host.markSelfWrite(input.workspaceId);
        return patchTaskRecord(mount.env.fs, input.taskPath, {
          executionSessionId: priorSessionId,
          updatedAt: mount.env.clock.now()
        });
      });
      emitTaskState(ctx, input.workspaceId, bound, "task.reject.resume");
      ctx.events.emit(
        "session.state",
        input.workspaceId,
        {
          sessionId: priorSessionId,
          state: probe.state,
          connectionId,
          taskPath: input.taskPath,
          reason: "task.reject.resume.isAlive",
          providerContextRestored: true
        },
        "self"
      );
      return {
        task: bound,
        session: {
          sessionId: priorSessionId,
          connectionId,
          adapterId,
          state: probe.state,
          cwd,
          providerContextRestored: true,
          restoreReason: "task.reject.resume.isAlive"
        }
      };
    }
  } catch (err) {
    if (!/Session not found/i.test(err instanceof Error ? err.message : String(err))) {
      throw err;
    }
  }
  if (!probe?.canResume) {
    throw new Error(
      `Managed Session ${priorSessionId} cannot restore its provider conversation; use task.replaceSession explicitly`
    );
  }
  rejectResumeNativeInFlight.add(priorSessionId);
  try {
    const handle = await ctx.runtime.resumeSession({
      sessionId: priorSessionId,
      runtimeWorkspace: { cwd },
      cwd,
      bootstrapPrompt: emptyBootstrap,
      currentTaskId: task.id
    });
    if (handle.sessionId !== priorSessionId) {
      throw new Error(
        `Native resume changed Session identity; expected ${priorSessionId}, got ${handle.sessionId}`
      );
    }
    if (!handle.connectionId || !handle.adapterId) {
      throw new Error(`Native resume returned an unbound managed Session: ${priorSessionId}`);
    }
    const bound = await ctx.mutations.run(input.workspaceId, async () => {
      ctx.host.markSelfWrite(input.workspaceId);
      const next = await patchTaskRecord(mount.env.fs, input.taskPath, {
        executionSessionId: handle.sessionId,
        updatedAt: mount.env.clock.now()
      });
      emitTaskState(ctx, input.workspaceId, next, "task.reject.resume");
      ctx.events.emit(
        "session.state",
        input.workspaceId,
        {
          sessionId: handle.sessionId,
          state: handle.state,
          connectionId: handle.connectionId,
          taskPath: input.taskPath,
          reason: "task.reject.resume.native",
          providerContextRestored: true
        },
        "self"
      );
      return next;
    });
    rejectResumeNativeInFlight.delete(priorSessionId);
    return {
      task: bound,
      session: {
        sessionId: handle.sessionId,
        connectionId: handle.connectionId,
        adapterId: handle.adapterId,
        state: handle.state,
        cwd,
        providerContextRestored: true,
        restoreReason: "task.reject.resume.native"
      }
    };
  } catch (err) {
    const message2 = err instanceof Error ? err.message : String(err);
    throw new Error(`Native resume failed for ${priorSessionId}: ${message2}`);
  }
}
async function parkTaskAfterRejectResumeFailure(ctx, input) {
  const mount = ctx.host.get(input.workspaceId);
  if (!mount) {
    if (input.sessionId) rejectResumeNativeInFlight.delete(input.sessionId);
    return;
  }
  try {
    if (input.sessionId) {
      try {
        await ctx.runtime.registry.update(input.sessionId, {
          lastError: `reject-resume restore failed: ${input.message}`
        });
      } catch {
      }
    }
    await ctx.mutations.run(input.workspaceId, async () => {
      ctx.host.markSelfWrite(input.workspaceId);
      const current = await loadTaskRecord(mount.env.fs, input.taskPath);
      if (current.state !== "running" && current.state !== "waiting") return;
      const summary = `${input.summaryPrefix ?? REJECT_RESUME_SESSION_FAILED_WAIT_SUMMARY} (${input.message})`;
      let next = current;
      if (current.state === "running") {
        next = await taskWait(mount.env, input.taskPath, {
          reason: "external",
          summary,
          code: SESSION_UNAVAILABLE_WAIT_CODE
        });
      } else {
        next = await patchTaskRecord(mount.env.fs, input.taskPath, {
          state: "waiting",
          wait: { reason: "external", summary, code: SESSION_UNAVAILABLE_WAIT_CODE },
          updatedAt: mount.env.clock.now()
        });
      }
      emitTaskState(ctx, input.workspaceId, next, "task.reject.resume.failed");
      ctx.events.emit(
        "session.state",
        input.workspaceId,
        {
          sessionId: input.sessionId,
          taskPath: input.taskPath,
          taskState: next.state,
          runtimeEvent: "task.reject.resume.failed",
          error: input.message,
          taskFailed: false
        },
        "service"
      );
    });
  } finally {
    if (input.sessionId) rejectResumeNativeInFlight.delete(input.sessionId);
  }
}
function emitTaskState(ctx, workspaceId, task, reason) {
  ctx.events.emit(
    "task.state",
    workspaceId,
    {
      path: task.path,
      id: task.id,
      state: task.state,
      roleId: task.assigneeRoleId,
      nodeIds: [...task.nodeIds],
      sessionId: task.executionSessionId,
      reason
    },
    "self"
  );
}
function requireWorkspaceId(ctx, p) {
  const explicit = optionalString2(p, "workspaceId");
  if (explicit) return explicit;
  const fg = ctx.host.getForegroundId();
  if (fg) return fg;
  throw new RpcError(-32602, "workspaceId is required when no foreground workspace is set");
}
function requireString(p, key2) {
  const v = p[key2];
  if (typeof v !== "string" || !v.trim()) {
    throw new RpcError(-32602, `Missing or invalid string param: ${key2}`);
  }
  return v.trim();
}
function assertAllowedParams(params, allowed, surface) {
  const unknown = Object.keys(params).filter((key2) => !allowed.has(key2)).sort();
  if (unknown.length > 0) {
    throw new RpcError(
      -32602,
      `${surface} received unknown parameter${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}`
    );
  }
}
function optionalString2(p, key2) {
  const v = p[key2];
  if (v === void 0 || v === null) return void 0;
  if (typeof v !== "string") throw new RpcError(-32602, `Invalid string param: ${key2}`);
  const t = v.trim();
  return t || void 0;
}
function optionalStringExact(p, key2) {
  const v = p[key2];
  if (v === void 0 || v === null) return void 0;
  if (typeof v !== "string") throw new RpcError(-32602, `Invalid string param: ${key2}`);
  return v;
}
function optionalStringArray(p, key2) {
  const v = p[key2];
  if (v === void 0 || v === null) return void 0;
  if (!Array.isArray(v) || !v.every((x) => typeof x === "string")) {
    throw new RpcError(-32602, `Invalid string[] param: ${key2}`);
  }
  return v;
}
function parseAcceptMode(raw) {
  if (!raw) return void 0;
  if (raw === "review-required" || raw === "auto-accept" || raw === "agent-decide") {
    return raw;
  }
  throw new RpcError(-32602, `Invalid acceptMode: ${raw}`);
}
function parseCallerKind(raw) {
  if (raw === "user" || raw === "role") return raw;
  throw new RpcError(-32602, `Invalid callerKind: ${raw}`);
}
function resolveNode2(tent, p) {
  return requireCanonicalNode(tent, requireString(p, "nodeId"));
}
function requireCanonicalNode(tent, nodeId) {
  if (!isNodeId(nodeId)) {
    throw new RpcError(-32602, `nodeId must be a canonical cx-* Node id: ${nodeId}`);
  }
  const node2 = tent.byId.get(nodeId);
  if (!node2) throw new RpcError(-32004, `Node not found: ${nodeId}`);
  return node2;
}
function projectNode(node2, includeBody, withChildren) {
  const title = typeof node2.fm.title === "string" ? node2.fm.title : void 0;
  const proj = {
    nodeId: node2.id,
    path: node2.path,
    name: node2.name,
    type: node2.type,
    tags: node2.tags,
    mode: node2.mode,
    archived: node2.archived,
    invalid: node2.invalid
  };
  if (title) proj.title = title;
  if (includeBody) {
    proj.bodyPreview = node2.body.slice(0, 500);
  }
  if (withChildren) {
    proj.children = node2.children.map((child) => projectNode(child, includeBody, true));
  }
  return proj;
}
function makeCommitIntegrator(ctx, workspaceRoot, task, options) {
  return async (commits) => {
    const refs = uniqueCommitRefs(commits);
    if (refs.length === 0) return;
    const taskPath = options.taskPath.trim() || task.path;
    const lockTask = await loadTaskRecordForIntegration(ctx, workspaceRoot, taskPath, task);
    const mount = requireMountByWorkspaceRoot(ctx, workspaceRoot);
    const lockContract = await resolveIntegrationContract(workspaceRoot, lockTask, mount.env.fs);
    await runIntegrationTargetFlight(workspaceRoot, lockContract.targetBranch, async () => {
      const liveTask = await loadTaskRecordForIntegration(
        ctx,
        workspaceRoot,
        taskPath,
        lockTask
      );
      const contract = await resolveIntegrationContract(workspaceRoot, liveTask, mount.env.fs);
      if (contract.targetBranch !== lockContract.targetBranch) {
        throw new Error(
          `Integration targetBranch changed under flight key (lock=${lockContract.targetBranch} live=${contract.targetBranch}); refuse Git write`
        );
      }
      let expected = options.expectedTargetHead;
      if (options.action === "task.accept") {
        const mount2 = requireMountByWorkspaceRoot(ctx, workspaceRoot);
        expected = await loadReadyTaskResultTargetHead(mount2.env.fs, liveTask);
      }
      const recovered = await assertIntegrationTargetHeadUnchanged(
        workspaceRoot,
        liveTask,
        refs,
        expected,
        mount.env.fs,
        { action: options.action }
      );
      if (recovered) return;
      if (ctx.integrateCommits) {
        await ctx.integrateCommits(
          workspaceRoot,
          refs,
          liveTask.assigneeRoleId ?? liveTask.executionSessionId ?? "unknown-executor"
        );
        return;
      }
      const writeContract = await resolveIntegrationContract(workspaceRoot, liveTask, mount.env.fs);
      await integrateWorkspaceCommits(writeContract, refs);
    });
  };
}
function requireMountByWorkspaceRoot(ctx, workspaceRoot) {
  const mounted = nodePath6.resolve(workspaceRoot);
  for (const info of ctx.host.list()) {
    const m = ctx.host.require(info.workspaceId);
    if (isSameWorkspaceRoot(m.workspaceRoot, mounted)) return m;
  }
  throw new Error(`No mounted workspace for integration re-read: ${workspaceRoot}`);
}
async function loadTaskRecordForIntegration(ctx, workspaceRoot, taskPath, fallback) {
  const path23 = taskPath.trim() || fallback.path;
  if (!path23) {
    throw new Error("Integration re-read requires taskPath");
  }
  const mount = requireMountByWorkspaceRoot(ctx, workspaceRoot);
  return loadTaskRecord(mount.env.fs, path23);
}
function uniqueCommitRefs(commits) {
  return [...new Set((commits ?? []).map((c) => c.trim()).filter(Boolean))];
}
async function snapshotIntegrationTargetHead(workspaceRoot, task, fs21) {
  const contract = await resolveIntegrationContract(workspaceRoot, task, fs21);
  return readRoleBranchTip(contract.workspace, contract.targetBranch);
}
async function assertIntegrationTargetHeadUnchanged(workspaceRoot, task, commits, expectedTargetHead, fs21, meta) {
  const contract = await resolveIntegrationContract(workspaceRoot, task, fs21);
  const current = await readRoleBranchTip(contract.workspace, contract.targetBranch);
  const expected = expectedTargetHead?.trim() || "";
  if (!expected) {
    throw new RpcError(
      RPC_LIFECYCLE,
      `${meta.action} refused: commit-bearing TaskResult is missing targetHead snapshot (incomplete row); resubmit so review can re-snapshot target ${contract.targetBranch} HEAD (task/result state unchanged; Git not touched)`,
      {
        code: "TARGET_MOVED",
        reason: "missing_snapshot",
        action: meta.action,
        taskPath: task.path,
        ...task.id ? { taskId: task.id } : {},
        targetBranch: contract.targetBranch,
        currentTargetHead: current
      }
    );
  }
  if (current !== expected) {
    const recovered = await findExactCompletedIntegrationAtTip(contract, commits, expected);
    if (recovered?.targetHead === current) return true;
    throw new RpcError(
      RPC_LIFECYCLE,
      `${meta.action} refused: integration target HEAD moved since TaskResult review (target=${contract.targetBranch} expected=${expected} current=${current}); re-review or resubmit (task/result state unchanged; Git not touched)`,
      {
        code: "TARGET_MOVED",
        reason: "head_moved",
        action: meta.action,
        taskPath: task.path,
        ...task.id ? { taskId: task.id } : {},
        targetBranch: contract.targetBranch,
        expectedTargetHead: expected,
        currentTargetHead: current
      }
    );
  }
  return false;
}
async function loadReadyTaskResultTargetHead(fs21, task) {
  if (!task.id || !isTaskId(task.id)) {
    throw new Error(`TaskResult target lookup requires a canonical Task id: ${task.path}.`);
  }
  const resultId = task.currentResultId;
  if (!resultId || !isTaskResultId(resultId)) {
    throw new Error(`TaskResult target lookup requires exact currentResultId: ${task.path}.`);
  }
  const result = await loadTaskResult(fs21, taskResultPathForTask(task.path, resultId));
  if (result.id !== resultId || result.taskId !== task.id || result.status !== "ready") {
    throw new Error(`TaskResult target lookup found a stale or mismatched current Result: ${resultId}.`);
  }
  return result.targetHead;
}
async function resolveIntegrationContract(workspaceRoot, task, fs21) {
  const mountedRoot = nodePath6.resolve(workspaceRoot);
  if (task.workspace) {
    const claimed = nodePath6.resolve(task.workspace);
    if (!isSameWorkspaceRoot(claimed, mountedRoot)) {
      throw new Error(
        `Task envelope workspace mismatch: envelope=${task.workspace} mounted=${workspaceRoot}`
      );
    }
  }
  const isTaskScoped = !task.assigneeRoleId;
  const registry = await loadRolesRegistry(fs21);
  let dispatcherLane;
  if (taskIsDownstream(task)) {
    const dispatcher = taskParentRoleId(task);
    const label2 = isTaskScoped ? `task ${task.id || task.path}` : `Role ${task.assigneeRoleId}`;
    if (!dispatcher) {
      throw new Error(
        `Sub task envelope missing durable parent Role for ${label2}; cannot resolve targetBranch`
      );
    }
    const dispatcherRole = registry.roles.find((role) => role.id === dispatcher);
    if (!dispatcherRole) throw new Error(`Parent Role not found in registry: ${dispatcher}`);
    dispatcherLane = await ensureRoleWorkspace(mountedRoot, dispatcherRole.name);
    if (task.targetBranch && task.targetBranch !== dispatcherLane.branch) {
      throw new Error(
        `Task envelope targetBranch mismatch for ${label2}: envelope=${task.targetBranch} expected=${dispatcherLane.branch}`
      );
    }
  }
  const executorRole = task.assigneeRoleId ? registry.roles.find((role) => role.id === task.assigneeRoleId) : void 0;
  if (task.assigneeRoleId && !executorRole) {
    throw new Error(`Role not found in registry: ${task.assigneeRoleId}`);
  }
  const real = isTaskScoped ? await ensureTaskWorkspace(mountedRoot, task.id || task.path, {
    ...dispatcherLane ? { targetBranch: dispatcherLane.branch } : {}
  }) : await ensureRoleWorkspace(mountedRoot, executorRole.name);
  const label = isTaskScoped ? `task ${task.id || task.path}` : `Role ${task.assigneeRoleId}`;
  if (task.branch && task.branch !== real.branch) {
    throw new Error(
      `Task envelope branch mismatch for ${label}: envelope=${task.branch} expected=${real.branch}`
    );
  }
  if (task.worktree) {
    const claimedWt = nodePath6.resolve(task.worktree);
    const realWt = nodePath6.resolve(real.worktree);
    if (!isSameWorkspaceRoot(claimedWt, realWt)) {
      throw new Error(
        `Task envelope worktree mismatch for ${label}: envelope=${task.worktree} expected=${real.worktree}`
      );
    }
  }
  if (taskIsDownstream(task)) {
    if (dispatcherLane.branch === real.branch) {
      throw new Error(
        `Sub task targetBranch must not equal assignee branch for ${label}: ${dispatcherLane.branch}`
      );
    }
    return { ...real, targetBranch: dispatcherLane.branch };
  }
  if (task.targetBranch && task.targetBranch !== real.targetBranch) {
    throw new Error(
      `Task envelope targetBranch mismatch for ${label}: envelope=${task.targetBranch} expected=${real.targetBranch}`
    );
  }
  return real;
}
async function ensureTaskWorkspaceLane(ctx, workspaceId, task) {
  const hasBase = Boolean(task.baseCommit?.trim());
  const hasAuthority = Boolean(task.integrationAuthority);
  const laneComplete = Boolean(
    task.worktree && task.branch && task.workspace && task.targetBranch
  );
  if (laneComplete && hasBase && hasAuthority) {
    return task;
  }
  const mount = ctx.host.require(workspaceId);
  return ctx.mutations.run(workspaceId, async () => {
    const current = await loadTaskRecord(mount.env.fs, task.path);
    const currentHasBase = Boolean(current.baseCommit?.trim());
    const currentHasAuthority = Boolean(current.integrationAuthority);
    const currentLaneComplete = Boolean(
      current.worktree && current.branch && current.workspace && current.targetBranch
    );
    if (currentLaneComplete && currentHasBase && currentHasAuthority) {
      return current;
    }
    const isTaskScoped = !current.assigneeRoleId;
    let taskTargetBranch;
    const registry = await loadRolesRegistry(mount.env.fs);
    if (isTaskScoped && taskIsDownstream(current)) {
      const dispatcher = taskParentRoleId(current);
      if (!dispatcher) {
        throw new Error(
          `Sub task ${current.id || current.path} is missing a durable parent Role.`
        );
      }
      const dispatcherRole = registry.roles.find((role) => role.id === dispatcher);
      if (!dispatcherRole) throw new Error(`Parent Role not found in registry: ${dispatcher}`);
      const dispatcherLane = await ensureRoleWorkspace(
        mount.workspaceRoot,
        dispatcherRole.name
      );
      taskTargetBranch = dispatcherLane.branch;
      const recordedTarget = current.targetBranch?.trim();
      if (recordedTarget && recordedTarget !== taskTargetBranch) {
        throw new Error(
          `Task envelope targetBranch mismatch: envelope=${recordedTarget} expected=${taskTargetBranch}`
        );
      }
    }
    const lane = currentLaneComplete ? {
      workspace: current.workspace,
      worktree: current.worktree,
      branch: current.branch,
      targetBranch: current.targetBranch
    } : isTaskScoped ? await ensureTaskWorkspaceIfGit(
      mount.workspaceRoot,
      current.id || current.path,
      { ...taskTargetBranch ? { targetBranch: taskTargetBranch } : {} }
    ) : await ensureRoleWorkspaceIfGit(
      mount.workspaceRoot,
      await requireRoleNameById(mount.env.fs, current.assigneeRoleId)
    );
    if (!lane) return current;
    let targetBranch = lane.targetBranch;
    if (taskIsDownstream(current)) {
      targetBranch = taskTargetBranch || (current.targetBranch || "").trim() || lane.targetBranch;
    }
    const patch = {
      updatedAt: mount.env.clock.now()
    };
    if (!currentLaneComplete) {
      patch.workspace = lane.workspace;
      patch.worktree = lane.worktree;
      patch.branch = lane.branch;
      patch.targetBranch = targetBranch;
    }
    if (!currentHasBase && !currentLaneComplete) {
      const fromEnsure = typeof lane.baseCommit === "string" ? lane.baseCommit.trim() : "";
      const tip = fromEnsure || await readRoleBranchTip(lane.workspace, lane.branch);
      patch.baseCommit = tip;
      if (!current.roleBranchBase?.trim()) {
        patch.roleBranchBase = tip;
      }
    }
    if (!currentHasAuthority) {
      if (!current.requester) {
        throw new Error(
          `Task ${current.id || current.path} missing requester; cannot derive integrationAuthority (actor=requester, mutator=service).`
        );
      }
      patch.integrationAuthority = deriveIntegrationAuthority({ requester: current.requester });
    }
    ctx.host.markSelfWrite(workspaceId);
    return patchTaskRecord(mount.env.fs, current.path, patch);
  });
}
async function findActiveExternalSessionForRole(ctx, workspaceId, roleId) {
  if (!roleId) return void 0;
  const all2 = await ctx.runtime.registry.list();
  return all2.find(
    (rec) => rec.workspace === workspaceId && rec.roleId === roleId && rec.state === "external"
  );
}
function projectStartSessionResult(workspaceId, taskPath, task, session, extra) {
  const cwd = extra?.cwd ?? session.runtimeWorkspace?.cwd ?? task.worktree ?? void 0;
  return {
    workspaceId,
    taskPath,
    task: projectTask(task),
    session: {
      sessionId: session.id,
      connectionId: session.connectionId,
      adapterId: session.adapterId,
      state: session.state,
      cwd
      // Do not expose pid in client projection by default — probe is internal.
    }
  };
}
async function buildSessionBootstrapPrompt(ctx, task, roots, roleFs) {
  const systemRoot = roots.systemRoot || systemRootFromWorkspace(roots.workspaceRoot);
  let roleDef;
  if (task.assigneeRoleId && roleFs) {
    try {
      const registry = await loadRolesRegistry(roleFs);
      roleDef = registry.roles.find((role) => role.id === task.assigneeRoleId);
    } catch {
      roleDef = void 0;
    }
  }
  const skillPrefix = composeManagedSkillBootstrapPrefix({
    packageRoot: ctx.packageRoot,
    roleId: task.assigneeRoleId,
    role: roleDef
  });
  const base = buildContextCardManagedBootstrap(task, {
    workspaceRoot: roots.workspaceRoot,
    systemRoot,
    sessionContextGeneration: roots.sessionContextGeneration,
    currentContextGeneration: roots.currentContextGeneration,
    tentTaskSection: skillPrefix,
    taskInputDelta: roots.taskInputDelta
  });
  return base;
}
function buildContextCardManagedBootstrap(task, roots) {
  const includeStablePrefix = shouldInjectStablePrefix({
    sessionContextGeneration: roots.sessionContextGeneration,
    currentContextGeneration: roots.currentContextGeneration
  });
  void decideStablePrefixInjection({
    sessionContextGeneration: roots.sessionContextGeneration,
    currentContextGeneration: roots.currentContextGeneration
  });
  const taskPackage2 = taskPackageForTask(task);
  const dynamicWrapperParts = [
    "--- Tent managed session execution ---",
    `Service status: this task is already claimed (state=${task.state || "running"}).`,
    "Managed path: Local Service already claimed this Task; a non-empty final assistant reply is submitted as a Result automatically."
  ];
  if (roots.taskInputDelta?.trim()) {
    dynamicWrapperParts.push(roots.taskInputDelta.trim());
  }
  const dynamicWrapper = dynamicWrapperParts.join("\n");
  const assembly = assembleManagedPrompt({
    workspaceRoot: roots.workspaceRoot,
    systemRoot: roots.systemRoot,
    agentsPointer: "AGENTS.md at workspace root (authoritative workspace agents file)",
    tentRoleSection: roots.tentRoleSection,
    rolePromptSection: roots.rolePromptSection,
    tentTaskSection: roots.tentTaskSection,
    contextGeneration: roots.currentContextGeneration,
    taskPackage: taskPackage2,
    dynamicWrapper,
    includeStablePrefix
  });
  if (includeStablePrefix) {
    return `--- Tent managed session bootstrap ---
${assembly.text}`;
  }
  return `--- Tent managed session delta (contextGeneration=${roots.currentContextGeneration}) ---
${assembly.text}`;
}
async function collectTaskBootstrapImageRefs(task) {
  const prompt = extractTaskPrompt(task);
  const claimBodies = task.nodeSnapshots.map((snapshot) => ({
    body: snapshot.body,
    notePath: nodeNotePath(snapshot.path)
  }));
  return collectBootstrapImageRefsFromTask({ prompt, claimBodies });
}
function projectTask(task) {
  if (!task.requester) {
    throw new RpcError(
      -32022,
      `Task ${task.id || task.path} is missing canonical requester`,
      { code: "TASK_REQUESTER_MISSING", taskPath: task.path }
    );
  }
  const requester = task.requester;
  const derivedAuthority = deriveIntegrationAuthority({ requester });
  const hasLane = Boolean(
    task.workspace || task.worktree || task.branch || task.targetBranch || task.baseCommit || derivedAuthority
  );
  const lane = hasLane ? {
    workspace: task.workspace,
    worktree: task.worktree,
    branch: task.branch,
    targetBranch: task.targetBranch,
    // Exact baseCommit only — never substitute roleBranchBase in the projection.
    ...task.baseCommit ? { baseCommit: task.baseCommit } : {},
    ...derivedAuthority ? { integrationAuthority: derivedAuthority } : {}
  } : void 0;
  const proj = {
    path: task.path,
    id: task.id,
    assigneeRoleId: task.assigneeRoleId,
    nodeIds: [...task.nodeIds],
    state: task.state,
    manifest: task.manifest,
    requester,
    acceptMode: task.acceptMode,
    executionSessionId: task.executionSessionId,
    wait: task.wait,
    currentResultId: task.currentResultId,
    statusDetail: task.statusDetail ? { ...task.statusDetail } : void 0,
    workspaceLane: lane,
    // Compact first-claim baseCommit capture audit.
    ...task.baseCommitCapture ? {
      baseCommitCapture: {
        source: task.baseCommitCapture.source,
        baseCommit: task.baseCommitCapture.baseCommit,
        actor: {
          kind: task.baseCommitCapture.actor.kind,
          id: task.baseCommitCapture.actor.id
        },
        capturedAt: task.baseCommitCapture.capturedAt
      }
    } : {},
    createdAt: task.createdAt,
    updatedAt: task.updatedAt,
    prompt: task.prompt,
    contextCard: task.contextCard,
    ...task.contextGeneration ? { contextGeneration: task.contextGeneration } : {}
  };
  return proj;
}
function projectTaskResult(d) {
  return {
    path: d.path,
    id: d.id,
    taskId: d.taskId,
    status: d.status,
    report: d.report,
    commits: d.commits,
    ...d.targetHead ? { targetHead: d.targetHead } : {},
    checks: d.checks.map((check) => ({ ...check })),
    artifactRefs: d.artifactRefs.map((ref) => ({ ...ref })),
    integrationMode: d.integrationMode,
    review: d.review ? { ...d.review } : void 0,
    createdAt: d.createdAt
  };
}
function assertDocsWriteCollaborationFields(frontmatter) {
  const protectedHit = PROTECTED_COLLAB_FIELDS.filter((k) => k in frontmatter);
  if (protectedHit.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write cannot set retired collaboration fields: ${protectedHit.join(", ")}. Collaboration truth lives on Task/TaskResult projections.`,
    { fields: protectedHit }
  );
}
function assertRawDocsWriteCollaborationFields(next) {
  const protectedHit = PROTECTED_COLLAB_FIELDS.filter((field) => field in next);
  if (protectedHit.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write raw cannot contain retired collaboration fields: ${protectedHit.join(", ")}. Collaboration truth lives on Task/TaskResult projections.`,
    { fields: protectedHit }
  );
}
function assertDocsModeMutable(node2, op) {
  if (isContentMutable(node2)) return;
  if (node2.invalid) {
    throw new RpcError(-32010, `${op} rejected: node is invalid`, {
      nodeId: node2.id,
      mode: node2.mode
    });
  }
  if (node2.mode === "archived" || node2.archived) {
    throw new RpcError(-32010, `${op} rejected: node is archived`, {
      nodeId: node2.id,
      mode: node2.mode
    });
  }
  throw new RpcError(-32010, `${op} rejected: node is not mutable`, {
    nodeId: node2.id,
    mode: node2.mode
  });
}
function assertReservedDocsWriteFields(frontmatter) {
  const hard = ["id", "mode", "archived", "resultId"].filter((k) => k in frontmatter);
  if (hard.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write cannot set reserved fields: ${hard.join(", ")}. Use docs.setMode for mode; Output resultId is a separate Node-authority provenance concern.`,
    { fields: hard }
  );
}
function assertSemanticDocsWriteFields(frontmatter) {
  const hit = SEMANTIC_DOCS_WRITE_FIELDS.filter((k) => k in frontmatter);
  if (hit.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write cannot set semantic fields: ${hit.join(", ")}. Use docs.setType / docs.tags.set / docs.tag.add / docs.tag.remove / relation.create|update|delete.`,
    { fields: hit }
  );
}
function assertRawDocsWriteReserved(disk, next) {
  const hard = ["id", "mode", "archived", "resultId"].filter(
    (field) => String(next[field] ?? "") !== String(disk[field] ?? "")
  );
  if (hard.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write cannot change reserved fields: ${hard.join(", ")}. Use docs.setMode for mode; Output resultId is a separate Node-authority provenance concern.`,
    { fields: hard }
  );
}
function assertRawDocsWriteSemantic(disk, next) {
  const changed = [];
  if (String(next.type ?? "") !== String(disk.type ?? "")) {
    changed.push("type");
  }
  const diskTags = JSON.stringify(normalizeTagsForCompare(disk.tags));
  const nextTags = JSON.stringify(normalizeTagsForCompare(next.tags));
  if (diskTags !== nextTags) {
    changed.push("tags");
  }
  const diskRelations = JSON.stringify(normalizeRelationsForCompare(disk.relations));
  const nextRelations = JSON.stringify(normalizeRelationsForCompare(next.relations));
  if (diskRelations !== nextRelations) {
    changed.push("relations");
  }
  if (changed.length === 0) return;
  throw new RpcError(
    -32010,
    `docs.write cannot change semantic fields: ${changed.join(", ")}. Use docs.setType / docs.tags.set / docs.tag.add / docs.tag.remove / relation.create|update|delete.`,
    { fields: changed }
  );
}
function normalizeTagsForCompare(value) {
  if (!Array.isArray(value)) return [];
  return value.filter((item) => typeof item === "string").map((t) => t.trim()).filter(Boolean).sort((a, b) => a.localeCompare(b));
}
function normalizeRelationsForCompare(value) {
  if (!Array.isArray(value)) return [];
  return value.filter((item) => isRecord10(item)).map((item) => {
    const keys = Object.keys(item).sort((a, b) => a.localeCompare(b));
    const ordered = {};
    for (const k of keys) ordered[k] = item[k];
    return ordered;
  }).sort((a, b) => String(a.id ?? "").localeCompare(String(b.id ?? "")));
}
function tagsFromFrontmatterData(data) {
  if (!Array.isArray(data.tags)) return [];
  return data.tags.filter((item) => typeof item === "string");
}

// src/service/auth.ts
import * as crypto2 from "node:crypto";

// src/runtime/session-token.ts
import * as crypto from "node:crypto";
function deriveSessionToken(secret, sessionId) {
  return crypto.createHmac("sha256", secret).update(`tent-session:${sessionId}`).digest("base64url");
}

// src/service/auth.ts
var AUTH_HEADER = "authorization";
var AUTH_TOKEN_HEADER = "x-tent-token";
var CALLER_SESSION_ID_HEADER = "x-tent-session-id";
var CALLER_SESSION_TOKEN_HEADER = "x-tent-session-token";
var CALLER_EXTERNAL_KEY_HEADER = "x-tent-external-key";
function generateServiceToken() {
  return crypto2.randomBytes(32).toString("base64url");
}
function extractRequestToken(headers) {
  const auth = headerValue(headers, AUTH_HEADER) ?? headerValue(headers, "Authorization");
  if (auth) {
    const m = /^Bearer\s+(.+)$/i.exec(auth.trim());
    if (m?.[1]) return m[1].trim();
  }
  const direct = headerValue(headers, AUTH_TOKEN_HEADER) ?? headerValue(headers, "X-Tent-Token");
  if (direct) return direct.trim() || void 0;
  return void 0;
}
function tokensEqual(expected, provided) {
  if (!provided) return false;
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(provided, "utf8");
  if (a.length !== b.length) return false;
  return crypto2.timingSafeEqual(a, b);
}
function extractCallerSessionContext(headers) {
  const sessionId = headerValue(headers, CALLER_SESSION_ID_HEADER)?.trim() || void 0;
  const sessionToken = headerValue(headers, CALLER_SESSION_TOKEN_HEADER)?.trim() || void 0;
  const externalKey = headerValue(headers, CALLER_EXTERNAL_KEY_HEADER)?.trim() || void 0;
  return { sessionId, sessionToken, externalKey };
}
function headerValue(headers, name) {
  const lower = name.toLowerCase();
  for (const [k, v] of Object.entries(headers)) {
    if (k.toLowerCase() !== lower) continue;
    if (Array.isArray(v)) return v[0];
    return v;
  }
  return void 0;
}

// src/service/data-dir.ts
import * as fs16 from "node:fs/promises";
import { isIP } from "node:net";
import * as os5 from "node:os";
import * as path15 from "node:path";
var MAX_SERVICE_ENDPOINT_CANDIDATES = 32;
var MAX_SERVICE_ENDPOINT_FILE_BYTES = 16 * 1024;
var SERVICE_ENDPOINT_PREFIX = "service.endpoint.";
var SERVICE_ENDPOINT_SUFFIX = ".json";
var INSTANCE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
function defaultServiceDataDir(env = process.env) {
  if (env.TENT_SERVICE_DATA_DIR) return path15.resolve(env.TENT_SERVICE_DATA_DIR);
  if (process.platform === "win32") {
    const base = env.APPDATA || path15.join(os5.homedir(), "AppData", "Roaming");
    return path15.join(base, "Tent");
  }
  if (process.platform === "darwin") {
    return path15.join(os5.homedir(), "Library", "Application Support", "Tent");
  }
  const xdg = env.XDG_STATE_HOME || path15.join(os5.homedir(), ".local", "state");
  return path15.join(xdg, "tent");
}
function serviceEndpointPath(dataDir, instanceId, startedAt) {
  const generation = endpointGenerationName(instanceId, startedAt);
  return path15.join(dataDir, generation);
}
function serviceBaseUrl(host, port) {
  const authorityHost = isIP(host) === 6 ? `[${host}]` : host;
  return `http://${authorityHost}:${port}`;
}
function isLoopbackServiceHost(host) {
  const normalized = host.trim().toLowerCase();
  const family = isIP(normalized);
  if (family === 4) return normalized.startsWith("127.");
  if (family === 6) {
    return normalized === "::1" || /^::ffff:127\./.test(normalized);
  }
  return false;
}
async function writeServiceEndpoint(dataDir, record) {
  const validated = parseServiceEndpointRecord(record);
  if (!validated) throw new Error("Invalid Local Tent Service endpoint record");
  const file = serviceEndpointPath(dataDir, validated.instanceId, validated.startedAt);
  await fs16.mkdir(dataDir, { recursive: true });
  const body = JSON.stringify(validated, null, 2) + "\n";
  if (Buffer.byteLength(body, "utf8") > MAX_SERVICE_ENDPOINT_FILE_BYTES) {
    throw new Error("Local Tent Service endpoint record exceeds the byte limit");
  }
  const temp = `${file}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
  await fs16.writeFile(temp, body, { encoding: "utf8", flag: "wx" });
  try {
    await fs16.link(temp, file);
  } finally {
    try {
      await fs16.rm(temp, { force: true });
    } catch {
    }
  }
  void cleanupOverflowServiceEndpointGenerations(dataDir);
  return file;
}
async function readServiceEndpoint(dataDir) {
  return (await readServiceEndpointCandidates(dataDir))[0] ?? null;
}
async function readServiceEndpointCandidates(dataDir) {
  const names = await newestEndpointGenerationNames(dataDir);
  const records = [];
  for (const name of names) {
    const file = path15.join(dataDir, name);
    try {
      const raw = await readBoundedEndpointFile(file);
      if (raw === null) continue;
      const value = parseServiceEndpointRecord(JSON.parse(raw));
      if (!value || endpointGenerationName(value.instanceId, value.startedAt) !== name) {
        continue;
      }
      records.push(value);
    } catch (error) {
      if (isNotFoundError(error) || error instanceof SyntaxError) continue;
      continue;
    }
  }
  return records;
}
async function removeServiceEndpoint(dataDir, expected) {
  try {
    await fs16.rm(
      serviceEndpointPath(dataDir, expected.instanceId, expected.startedAt),
      { force: true }
    );
  } catch {
  }
}
function parseServiceEndpointRecord(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
  const data = value;
  if (typeof data.instanceId !== "string" || !INSTANCE_ID_PATTERN.test(data.instanceId) || !Number.isInteger(data.pid) || (data.pid ?? 0) <= 0 || !Number.isInteger(data.port) || (data.port ?? 0) <= 0 || (data.port ?? 0) > 65535 || typeof data.host !== "string" || !isLoopbackServiceHost(data.host) || typeof data.startedAt !== "string" || !isCanonicalServiceStartedAt(data.startedAt) || typeof data.version !== "string" || data.token !== void 0 && typeof data.token !== "string") {
    return null;
  }
  return data;
}
function endpointGenerationName(instanceId, startedAt) {
  if (!INSTANCE_ID_PATTERN.test(instanceId)) {
    throw new Error("Invalid Local Tent Service instance id");
  }
  if (!isCanonicalServiceStartedAt(startedAt)) {
    throw new Error("Invalid Local Tent Service startedAt");
  }
  const startedMs = Date.parse(startedAt);
  return `${SERVICE_ENDPOINT_PREFIX}${Math.trunc(startedMs).toString().padStart(16, "0")}.${instanceId}${SERVICE_ENDPOINT_SUFFIX}`;
}
function isCanonicalServiceStartedAt(value) {
  const startedMs = Date.parse(value);
  return Number.isFinite(startedMs) && startedMs >= 0 && new Date(startedMs).toISOString() === value;
}
async function newestEndpointGenerationNames(dataDir) {
  const newest = [];
  let directory;
  try {
    directory = await fs16.opendir(dataDir);
  } catch (error) {
    if (isNotFoundError(error)) return [];
    throw error;
  }
  for await (const entry of directory) {
    if (!entry.isFile() || !isEndpointGenerationName(entry.name)) continue;
    const insertAt = newest.findIndex((name) => entry.name > name);
    if (insertAt < 0) newest.push(entry.name);
    else newest.splice(insertAt, 0, entry.name);
    if (newest.length > MAX_SERVICE_ENDPOINT_CANDIDATES) newest.pop();
  }
  return newest;
}
function isEndpointGenerationName(name) {
  if (!name.startsWith(SERVICE_ENDPOINT_PREFIX) || !name.endsWith(SERVICE_ENDPOINT_SUFFIX)) {
    return false;
  }
  const middle = name.slice(SERVICE_ENDPOINT_PREFIX.length, -SERVICE_ENDPOINT_SUFFIX.length);
  const separator = middle.indexOf(".");
  if (separator <= 0) return false;
  const timestamp2 = middle.slice(0, separator);
  const instanceId = middle.slice(separator + 1);
  return /^\d{16}$/.test(timestamp2) && INSTANCE_ID_PATTERN.test(instanceId);
}
async function readBoundedEndpointFile(file) {
  let handle;
  try {
    handle = await fs16.open(file, "r");
    const buffer = Buffer.allocUnsafe(MAX_SERVICE_ENDPOINT_FILE_BYTES + 1);
    let used = 0;
    while (used < buffer.length) {
      const { bytesRead } = await handle.read(
        buffer,
        used,
        buffer.length - used,
        null
      );
      if (bytesRead === 0) break;
      used += bytesRead;
    }
    if (used === 0 || used > MAX_SERVICE_ENDPOINT_FILE_BYTES) return null;
    return buffer.subarray(0, used).toString("utf8");
  } finally {
    await handle?.close().catch(() => void 0);
  }
}
async function cleanupOverflowServiceEndpointGenerations(dataDir) {
  try {
    const newest = [];
    const directory = await fs16.opendir(dataDir);
    for await (const entry of directory) {
      if (!entry.isFile() || !isEndpointGenerationName(entry.name)) continue;
      const insertAt = newest.findIndex((name) => entry.name > name);
      if (insertAt < 0) {
        if (newest.length >= MAX_SERVICE_ENDPOINT_CANDIDATES) {
          await fs16.rm(path15.join(dataDir, entry.name), { force: true }).catch(() => void 0);
        } else {
          newest.push(entry.name);
        }
        continue;
      }
      newest.splice(insertAt, 0, entry.name);
      if (newest.length > MAX_SERVICE_ENDPOINT_CANDIDATES) {
        const overflow = newest.pop();
        if (overflow) {
          await fs16.rm(path15.join(dataDir, overflow), { force: true }).catch(() => void 0);
        }
      }
    }
  } catch {
  }
}

// src/service/http-server.ts
function isServiceRpcError(error) {
  if (error instanceof RpcError) return true;
  if (!(error instanceof Error)) return false;
  if (error.name === "RpcError" || error.constructor?.name === "RpcError") {
    const code = error.code;
    return typeof code === "number";
  }
  return false;
}
function isTaskLifecycleErrorHttp(error) {
  if (!(error instanceof Error)) return false;
  return error.name === "TaskLifecycleError" || /^Invalid task transition:/.test(error.message);
}
var FETCH_BLOCKED_PORTS = /* @__PURE__ */ new Set([
  1,
  7,
  9,
  11,
  13,
  15,
  17,
  19,
  20,
  21,
  22,
  23,
  25,
  37,
  42,
  43,
  53,
  69,
  77,
  79,
  87,
  95,
  101,
  102,
  103,
  104,
  109,
  110,
  111,
  113,
  115,
  117,
  119,
  123,
  135,
  137,
  139,
  143,
  161,
  179,
  389,
  427,
  465,
  512,
  513,
  514,
  515,
  526,
  530,
  531,
  532,
  540,
  548,
  554,
  556,
  563,
  587,
  601,
  636,
  989,
  990,
  993,
  995,
  1719,
  1720,
  1723,
  2049,
  3659,
  4045,
  4190,
  5060,
  5061,
  6e3,
  6566,
  6665,
  6666,
  6667,
  6668,
  6669,
  6679,
  6697,
  10080
]);
var MAX_RPC_BODY_BYTES = 36 * 1024 * 1024;
var MAX_SSE_QUEUE_BYTES = 1024 * 1024;
var RequestBodyTooLargeError = class extends Error {
  constructor(maxBytes) {
    super(`RPC request body exceeds ${maxBytes} bytes`);
    this.maxBytes = maxBytes;
    this.name = "RequestBodyTooLargeError";
  }
};
function isFetchBlockedPort(port) {
  return FETCH_BLOCKED_PORTS.has(port);
}
async function listenOnFetchCompatiblePort(server, host, preferredPort = 0) {
  for (let attempt = 0; attempt < 20; attempt++) {
    await listen(server, preferredPort, host);
    const addr = server.address();
    if (!addr || typeof addr === "string") {
      await closeServer(server);
      throw new Error("Failed to bind Local Tent Service HTTP server");
    }
    const port = addr.port;
    if (!isFetchBlockedPort(port)) return port;
    await closeServer(server);
    if (preferredPort !== 0) {
      throw new Error(`Local Tent Service port ${port} is blocked by Fetch clients`);
    }
  }
  throw new Error("Failed to allocate a Fetch-compatible Local Tent Service port");
}
async function createServiceHttpServer(options) {
  const host = options.host ?? "127.0.0.1";
  const preferredPort = options.port ?? 0;
  if (!isLoopbackServiceHost(host)) {
    throw new Error(
      `Local Tent Service host must be a literal loopback address (127.0.0.0/8 or ::1), got: ${host}`
    );
  }
  const closeSseConnections = /* @__PURE__ */ new Set();
  const activeResponses = /* @__PURE__ */ new Set();
  const server = http.createServer(async (req, res) => {
    activeResponses.add(res);
    const releaseResponse = () => activeResponses.delete(res);
    res.once("finish", releaseResponse);
    res.once("close", releaseResponse);
    try {
      await handleRequest(req, res, options, closeSseConnections);
    } catch (error) {
      if (!res.headersSent) {
        res.writeHead(500, { "content-type": "application/json; charset=utf-8" });
        res.end(JSON.stringify({ error: "Internal Server Error" }));
      } else {
        res.destroy();
      }
    }
  });
  server.requestTimeout = 6e4;
  server.headersTimeout = 1e4;
  server.keepAliveTimeout = 5e3;
  const port = await listenOnFetchCompatiblePort(server, host, preferredPort);
  let closePromise = null;
  return {
    server,
    host,
    port,
    url: serviceBaseUrl(host, port),
    close: () => {
      if (closePromise) return closePromise;
      closePromise = closeServer(server);
      for (const response of activeResponses) {
        if (!response.headersSent) response.setHeader("connection", "close");
      }
      for (const closeSse of [...closeSseConnections]) closeSse();
      server.closeIdleConnections?.();
      return closePromise;
    }
  };
}
function listen(server, port, host) {
  return new Promise((resolve15, reject) => {
    const onError = (error) => {
      server.off("listening", onListening);
      reject(error);
    };
    const onListening = () => {
      server.off("error", onError);
      resolve15();
    };
    server.once("error", onError);
    server.once("listening", onListening);
    server.listen(port, host);
  });
}
function closeServer(server) {
  return new Promise((resolve15, reject) => {
    server.close((err) => err ? reject(err) : resolve15());
  });
}
async function handleRequest(req, res, options, closeSseConnections) {
  const { ctx, events, token } = options;
  const url = new URL(req.url || "/", `http://${req.headers.host || "127.0.0.1"}`);
  if (req.method === "GET" && (url.pathname === "/health" || url.pathname === "/")) {
    const body = await dispatchMethod(ctx, "service.health", {});
    writeJson2(res, 200, body);
    return;
  }
  if (req.method === "GET" && url.pathname === "/events") {
    if (!authorize(req, token)) {
      writeJson2(res, 401, { error: "Unauthorized: invalid or missing service token" });
      return;
    }
    handleSse(req, res, events, closeSseConnections);
    return;
  }
  if (req.method === "POST" && (url.pathname === "/rpc" || url.pathname === "/")) {
    if (!authorize(req, token)) {
      writeJson2(res, 401, {
        jsonrpc: "2.0",
        id: null,
        error: { code: RPC_UNAUTHORIZED, message: "Unauthorized: invalid or missing service token" }
      });
      return;
    }
    let raw;
    try {
      raw = await readBody(req, MAX_RPC_BODY_BYTES);
    } catch (error) {
      res.setHeader("connection", "close");
      if (error instanceof RequestBodyTooLargeError) {
        writeJson2(res, 413, {
          jsonrpc: "2.0",
          id: null,
          error: {
            code: -32013,
            message: "RPC request body too large",
            data: { maxBytes: error.maxBytes }
          }
        });
      } else {
        writeJson2(res, 400, {
          jsonrpc: "2.0",
          id: null,
          error: { code: -32700, message: "Request body read failed" }
        });
      }
      return;
    }
    let parsed;
    try {
      parsed = JSON.parse(raw || "{}");
    } catch {
      writeJson2(res, 400, {
        jsonrpc: "2.0",
        id: null,
        error: { code: -32700, message: "Parse error" }
      });
      return;
    }
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      writeJson2(res, 200, {
        jsonrpc: "2.0",
        id: null,
        error: { code: -32600, message: "Invalid Request" }
      });
      return;
    }
    const message2 = parsed;
    const id = isRpcId(message2.id) ? message2.id ?? null : null;
    if (message2.jsonrpc !== "2.0" || !message2.method || typeof message2.method !== "string" || !isRpcId(message2.id)) {
      writeJson2(res, 200, {
        jsonrpc: "2.0",
        id,
        error: { code: -32600, message: "Invalid Request: method required" }
      });
      return;
    }
    if (message2.params !== void 0 && (!message2.params || typeof message2.params !== "object")) {
      writeJson2(res, 200, {
        jsonrpc: "2.0",
        id,
        error: { code: -32602, message: "Invalid params: expected object or array" }
      });
      return;
    }
    const params = message2.params === void 0 ? void 0 : Array.isArray(message2.params) ? Object.fromEntries(
      message2.params.map((v, i) => [String(i), v])
    ) : typeof message2.params === "object" && message2.params ? message2.params : void 0;
    try {
      const suppliedCaller = extractCallerSessionContext(req.headers);
      const callerSessionId = suppliedCaller.sessionId && tokensEqual(
        deriveSessionToken(token, suppliedCaller.sessionId),
        suppliedCaller.sessionToken
      ) ? suppliedCaller.sessionId : void 0;
      const result = await dispatchMethod(ctx, message2.method, params, {
        callerSessionId,
        callerExternalKey: suppliedCaller.externalKey
      });
      writeJson2(res, 200, { jsonrpc: "2.0", id, result });
    } catch (error) {
      if (isServiceRpcError(error)) {
        writeJson2(res, 200, {
          jsonrpc: "2.0",
          id,
          error: { code: error.code, message: error.message, data: error.data }
        });
        return;
      }
      if (isTaskLifecycleErrorHttp(error)) {
        const dataCode = typeof error.code === "string" ? error.code : "INVALID_TRANSITION";
        writeJson2(res, 200, {
          jsonrpc: "2.0",
          id,
          error: {
            code: RPC_LIFECYCLE,
            message: error.message,
            data: { code: dataCode }
          }
        });
        return;
      }
      const messageText = error instanceof Error ? error.message : String(error);
      writeJson2(res, 200, {
        jsonrpc: "2.0",
        id,
        error: { code: -32e3, message: messageText }
      });
    }
    return;
  }
  writeJson2(res, 404, { error: "not found" });
}
function authorize(req, expectedToken) {
  const provided = extractRequestToken(req.headers);
  return tokensEqual(expectedToken, provided);
}
function handleSse(req, res, events, closeSseConnections) {
  res.writeHead(200, {
    "content-type": "text/event-stream; charset=utf-8",
    "cache-control": "no-cache",
    connection: "keep-alive"
  });
  let closed = false;
  let blocked = false;
  let queuedBytes = 0;
  const queue3 = [];
  let unsubscribe = () => {
  };
  const cleanup = () => {
    if (closed) return;
    closed = true;
    clearInterval(heartbeat);
    unsubscribe();
    queue3.length = 0;
    queuedBytes = 0;
    closeSseConnections.delete(close);
    req.off("close", cleanup);
    res.off("close", cleanup);
    res.off("drain", flush);
  };
  const close = () => {
    if (closed) return;
    cleanup();
    res.destroy();
  };
  const send = (payload) => {
    if (closed) return;
    const bytes = Buffer.byteLength(payload);
    if (bytes > MAX_SSE_QUEUE_BYTES || blocked && queuedBytes + bytes > MAX_SSE_QUEUE_BYTES) {
      close();
      return;
    }
    if (blocked) {
      queue3.push({ payload, bytes });
      queuedBytes += bytes;
      return;
    }
    try {
      blocked = !res.write(payload);
    } catch {
      close();
    }
  };
  function flush() {
    if (closed) return;
    blocked = false;
    while (queue3.length > 0) {
      const next = queue3.shift();
      queuedBytes -= next.bytes;
      try {
        if (!res.write(next.payload)) {
          blocked = true;
          return;
        }
      } catch {
        close();
        return;
      }
    }
  }
  const onEvent = (event) => {
    send(`event: ${event.type}
data: ${JSON.stringify(event)}

`);
  };
  unsubscribe = events.subscribe(onEvent);
  const heartbeat = setInterval(() => send(": ping\n\n"), 15e3);
  closeSseConnections.add(close);
  req.once("close", cleanup);
  res.once("close", cleanup);
  res.on("drain", flush);
  send(": ok\n\n");
}
function writeJson2(res, status, body) {
  const payload = JSON.stringify(body);
  res.writeHead(status, {
    "content-type": "application/json; charset=utf-8",
    "content-length": Buffer.byteLength(payload)
  });
  res.end(payload);
}
function readBody(req, maxBytes) {
  return new Promise((resolve15, reject) => {
    const declaredLength = Number(req.headers["content-length"] ?? 0);
    if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
      req.resume();
      reject(new RequestBodyTooLargeError(maxBytes));
      return;
    }
    const chunks = [];
    let total = 0;
    let settled = false;
    const finish = (action) => {
      if (settled) return;
      settled = true;
      req.off("data", onData);
      req.off("end", onEnd);
      req.off("error", onError);
      req.off("aborted", onAborted);
      action();
    };
    const onData = (chunk) => {
      const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
      total += buffer.byteLength;
      if (total > maxBytes) {
        finish(() => reject(new RequestBodyTooLargeError(maxBytes)));
        req.resume();
        return;
      }
      chunks.push(buffer);
    };
    const onEnd = () => finish(() => resolve15(Buffer.concat(chunks, total).toString("utf8")));
    const onError = (error) => finish(() => reject(error));
    const onAborted = () => finish(() => reject(new Error("request aborted")));
    req.on("data", onData);
    req.on("end", onEnd);
    req.on("error", onError);
    req.on("aborted", onAborted);
  });
}
function isRpcId(id) {
  return id === void 0 || id === null || typeof id === "string" || typeof id === "number";
}

// src/service/events.ts
import { randomBytes as randomBytes2 } from "node:crypto";
var EventBus = class {
  constructor() {
    this.listeners = /* @__PURE__ */ new Set();
    this.seq = 0;
  }
  subscribe(listener) {
    this.listeners.add(listener);
    return () => {
      this.listeners.delete(listener);
    };
  }
  emit(type, workspaceId, payload, source = "service") {
    const event = {
      id: `ev-${Date.now().toString(36)}-${(++this.seq).toString(36)}-${randomBytes2(3).toString("hex")}`,
      type,
      workspaceId,
      ts: (/* @__PURE__ */ new Date()).toISOString(),
      source,
      payload
    };
    for (const listener of this.listeners) {
      try {
        listener(event);
      } catch {
      }
    }
    return event;
  }
  listenerCount() {
    return this.listeners.size;
  }
};

// src/service/workspace-host.ts
import { createHash as createHash5 } from "node:crypto";
import { watch } from "node:fs";
import * as fs17 from "node:fs/promises";
import * as path16 from "node:path";
var WORKSPACE_ID_DIGEST_LEN = 12;
var WorkspaceHost = class {
  constructor(options) {
    this.mounts = /* @__PURE__ */ new Map();
    this.foregroundId = null;
    this.watchTimers = /* @__PURE__ */ new Map();
    /** Distinct admitted paths in the current debounce window, keyed by path identity. */
    this.watchPendingPaths = /* @__PURE__ */ new Map();
    this.housekeepingTimers = /* @__PURE__ */ new Map();
    this.housekeepingRunning = /* @__PURE__ */ new Set();
    this.events = options.events;
    this.clock = options.clock ?? { now: () => (/* @__PURE__ */ new Date()).toISOString() };
    this.watchFn = options.watchFn ?? watch;
    this.watchDebounceMs = options.watchDebounceMs ?? 50;
    this.housekeeper = options.housekeeper;
    this.housekeepingInitialDelayMs = options.housekeepingInitialDelayMs ?? 6e4;
    this.housekeepingIntervalMs = options.housekeepingIntervalMs ?? 24 * 60 * 60 * 1e3;
  }
  list() {
    return [...this.mounts.values()].map((m) => this.toInfo(m));
  }
  get(workspaceId) {
    return this.mounts.get(workspaceId);
  }
  require(workspaceId) {
    const m = this.mounts.get(workspaceId);
    if (!m) throw new Error(`Workspace not mounted: ${workspaceId}`);
    return m;
  }
  getForegroundId() {
    return this.foregroundId;
  }
  async mount(workspaceRoot, opts) {
    const resolved = path16.resolve(workspaceRoot);
    let root;
    try {
      root = await fs17.realpath(resolved);
    } catch (error) {
      const err = error;
      if (err?.code === "ENOENT") {
        throw new Error(`Workspace path does not exist: ${resolved}`);
      }
      throw error;
    }
    const systemRoot = systemRootFromWorkspace(root);
    const indexPath = path16.join(systemRoot, INDEX_PATH);
    try {
      await fs17.access(indexPath);
    } catch {
      throw new Error(
        `No in-workspace Tent at ${systemRoot}. Expected ${TENT_SYSTEM_DIR}/${INDEX_PATH}.`
      );
    }
    for (const existing of this.mounts.values()) {
      if (isSameWorkspaceRoot(existing.workspaceRoot, root)) {
        return this.toInfo(existing);
      }
    }
    const workspaceId = opts?.workspaceId?.trim() || makeWorkspaceId(root);
    if (this.mounts.has(workspaceId)) {
      throw new Error(`workspaceId already mounted: ${workspaceId}`);
    }
    const tentName = opts?.tentName?.trim() || path16.basename(root) || "tent";
    const fsa = new NodeFs(systemRoot);
    const env = {
      fs: fsa,
      clock: this.clock,
      tentName,
      tentRoot: systemRoot
    };
    const mount = {
      workspaceId,
      workspaceRoot: root,
      systemRoot,
      tentName,
      env,
      suppressWatchUntil: 0,
      suppressWindows: []
    };
    mount.watcher = this.startWatch(mount);
    this.mounts.set(workspaceId, mount);
    this.scheduleHousekeeping(mount, this.housekeepingInitialDelayMs);
    if (!this.foregroundId) {
      this.foregroundId = workspaceId;
      this.events.emit("workspace.switched", workspaceId, {
        workspaceId,
        workspaceRoot: root,
        reason: "mount-default-foreground"
      });
    }
    this.events.emit("service.health", workspaceId, {
      action: "workspace.mounted",
      workspaceId,
      workspaceRoot: root
    });
    return this.toInfo(mount);
  }
  async unmount(workspaceId) {
    const mount = this.mounts.get(workspaceId);
    if (!mount) return;
    this.stopWatch(mount);
    this.stopHousekeeping(workspaceId);
    this.mounts.delete(workspaceId);
    if (this.foregroundId === workspaceId) {
      const next = this.mounts.keys().next();
      this.foregroundId = next.done ? null : next.value;
      if (this.foregroundId) {
        this.events.emit("workspace.switched", this.foregroundId, {
          workspaceId: this.foregroundId,
          reason: "unmount-reselect"
        });
      }
    }
    this.events.emit("service.health", workspaceId, {
      action: "workspace.unmounted",
      workspaceId
    });
  }
  setForeground(workspaceId) {
    const mount = this.require(workspaceId);
    if (this.foregroundId !== workspaceId) {
      this.foregroundId = workspaceId;
      this.events.emit("workspace.switched", workspaceId, {
        workspaceId,
        workspaceRoot: mount.workspaceRoot,
        reason: "setForeground"
      });
    }
    return this.toInfo(mount);
  }
  /** Call before service-originated disk writes to reduce watch self-echo noise. */
  markSelfWrite(workspaceId, holdMs = 200, paths) {
    const mount = this.mounts.get(workspaceId);
    if (!mount) return;
    const now = Date.now();
    const until = now + Math.max(0, holdMs);
    let pathKeys = null;
    if (paths !== void 0) {
      const normalized = (Array.isArray(paths) ? paths : [paths]).map(normalizeWatchPath).filter((value) => value.length > 0).map(watchPathKey);
      pathKeys = normalized.length > 0 ? [...new Set(normalized)] : null;
    }
    mount.suppressWindows = mount.suppressWindows.filter((window) => window.until > now);
    mount.suppressWindows.push({ until, pathKeys });
    if (pathKeys === null) {
      mount.suppressWatchUntil = Math.max(mount.suppressWatchUntil, until);
    }
  }
  async dispose() {
    for (const id of [...this.mounts.keys()]) {
      await this.unmount(id);
    }
    for (const timer of this.watchTimers.values()) clearTimeout(timer);
    this.watchTimers.clear();
    this.watchPendingPaths.clear();
    for (const timer of this.housekeepingTimers.values()) clearTimeout(timer);
    this.housekeepingTimers.clear();
    this.housekeepingRunning.clear();
  }
  toInfo(m) {
    return {
      workspaceId: m.workspaceId,
      workspaceRoot: m.workspaceRoot,
      systemRoot: m.systemRoot,
      tentName: m.tentName,
      foreground: this.foregroundId === m.workspaceId
    };
  }
  startWatch(mount) {
    const watcher = this.watchFn(
      mount.systemRoot,
      { recursive: true },
      (_event, filename) => {
        if (!filename) return;
        const rel = normalizeWatchPath(String(filename));
        if (rel === "mutation.lock" || rel.endsWith("/mutation.lock")) return;
        if (rel === "attachments/.gc-state.json") return;
        if (this.isWatchSuppressed(mount, rel)) return;
        let pending = this.watchPendingPaths.get(mount.workspaceId);
        if (!pending) {
          pending = /* @__PURE__ */ new Map();
          this.watchPendingPaths.set(mount.workspaceId, pending);
        }
        const key2 = watchPathKey(rel);
        if (!pending.has(key2)) pending.set(key2, rel);
        const prev = this.watchTimers.get(mount.workspaceId);
        if (prev) clearTimeout(prev);
        this.watchTimers.set(
          mount.workspaceId,
          setTimeout(() => {
            this.watchTimers.delete(mount.workspaceId);
            const admitted = this.watchPendingPaths.get(mount.workspaceId);
            this.watchPendingPaths.delete(mount.workspaceId);
            if (!admitted || admitted.size === 0) return;
            this.flushWatchPaths(mount, admitted.values());
          }, this.watchDebounceMs)
        );
      }
    );
    watcher.on("error", () => {
    });
    return watcher;
  }
  flushWatchPaths(mount, paths) {
    let emittedTaskState = false;
    for (const rel of paths) {
      if (isTempWatchPath(rel)) {
        if (!emittedTaskState) {
          this.events.emit("task.state", mount.workspaceId, {
            reason: "watch",
            path: rel
          });
          emittedTaskState = true;
        }
        continue;
      }
      this.events.emit("node.changed", mount.workspaceId, {
        reason: "watch",
        path: rel
      });
    }
  }
  isWatchSuppressed(mount, rel) {
    const now = Date.now();
    mount.suppressWindows = mount.suppressWindows.filter((window) => window.until > now);
    mount.suppressWatchUntil = mount.suppressWindows.filter((window) => window.pathKeys === null).reduce((latest, window) => Math.max(latest, window.until), 0);
    const relKey = watchPathKey(rel);
    for (const window of mount.suppressWindows) {
      if (window.pathKeys === null) return true;
      for (const pathKey of window.pathKeys) {
        if (relKey === pathKey || relKey.startsWith(pathKey + "/")) return true;
      }
    }
    return false;
  }
  stopWatch(mount) {
    const timer = this.watchTimers.get(mount.workspaceId);
    if (timer) {
      clearTimeout(timer);
      this.watchTimers.delete(mount.workspaceId);
    }
    this.watchPendingPaths.delete(mount.workspaceId);
    try {
      mount.watcher?.close();
    } catch {
    }
    mount.watcher = void 0;
  }
  scheduleHousekeeping(mount, delayMs) {
    if (!this.housekeeper || this.housekeepingIntervalMs <= 0) return;
    this.stopHousekeeping(mount.workspaceId);
    const timer = setTimeout(() => {
      this.housekeepingTimers.delete(mount.workspaceId);
      void this.runHousekeeping(mount.workspaceId);
    }, Math.max(0, delayMs));
    timer.unref?.();
    this.housekeepingTimers.set(mount.workspaceId, timer);
  }
  async runHousekeeping(workspaceId) {
    const mount = this.mounts.get(workspaceId);
    if (!mount || !this.housekeeper || this.housekeepingRunning.has(workspaceId)) return;
    this.housekeepingRunning.add(workspaceId);
    try {
      await this.housekeeper(mount);
    } catch {
    } finally {
      this.housekeepingRunning.delete(workspaceId);
      const current = this.mounts.get(workspaceId);
      if (current) this.scheduleHousekeeping(current, this.housekeepingIntervalMs);
    }
  }
  stopHousekeeping(workspaceId) {
    const timer = this.housekeepingTimers.get(workspaceId);
    if (timer) clearTimeout(timer);
    this.housekeepingTimers.delete(workspaceId);
  }
};
function normalizeWatchPath(value) {
  return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
}
function watchPathKey(value) {
  return process.platform === "win32" ? value.toLowerCase() : value;
}
function isTempWatchPath(value) {
  const key2 = watchPathKey(value);
  return key2 === "temp" || key2.startsWith("temp/");
}
function makeWorkspaceId(workspaceRoot) {
  const base = path16.basename(workspaceRoot).replace(/[^a-zA-Z0-9._-]+/g, "-") || "ws";
  const identity = process.platform === "win32" ? workspaceRoot.toLowerCase() : workspaceRoot;
  const digest = createHash5("sha256").update(identity).digest("base64url").slice(0, WORKSPACE_ID_DIGEST_LEN);
  return `ws-${base}-${digest}`;
}

// src/service/protocol.ts
var TENT_SERVICE_PROTOCOL_VERSION = 10;

// src/service/tool-approval-store.ts
import * as fs18 from "node:fs/promises";
import * as path17 from "node:path";
function resolveToolApprovalWorkspaceId(sessionWorkspace) {
  if (typeof sessionWorkspace !== "string") return null;
  const workspaceId = sessionWorkspace.trim();
  return workspaceId || null;
}
function cloneApproval(item) {
  return {
    ...item,
    options: item.options.map((option) => ({ ...option }))
  };
}
var TOOL_APPROVAL_STATUSES = /* @__PURE__ */ new Set([
  "pending",
  "approved",
  "denied",
  "expired"
]);
function isRecord11(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function isRequiredString3(value) {
  return typeof value === "string" && value.trim().length > 0;
}
function isOptionalString2(value) {
  return value === void 0 || typeof value === "string";
}
function isValidDate4(value) {
  return Number.isFinite(Date.parse(value));
}
function parseApproval(value) {
  if (!isRecord11(value)) return null;
  const {
    id,
    workspaceId,
    sessionId,
    taskId,
    taskPath,
    role,
    toolTitle,
    toolCallId,
    options,
    status,
    createdAt,
    expiresAt,
    resolvedAt,
    resolvedBy
  } = value;
  if (!isRequiredString3(id) || !isRequiredString3(workspaceId) || !isRequiredString3(sessionId) || !isRequiredString3(toolTitle) || !isRequiredString3(createdAt) || !isRequiredString3(expiresAt) || !isValidDate4(createdAt) || !isValidDate4(expiresAt) || typeof status !== "string" || !TOOL_APPROVAL_STATUSES.has(status) || !Array.isArray(options) || !isOptionalString2(taskId) || !isOptionalString2(taskPath) || !isOptionalString2(role) || !isOptionalString2(toolCallId) || !isOptionalString2(resolvedAt) || !isOptionalString2(resolvedBy) || resolvedAt !== void 0 && !isValidDate4(resolvedAt)) {
    return null;
  }
  const parsedOptions = [];
  for (const option of options) {
    if (!isRecord11(option) || !isRequiredString3(option.optionId) || !isOptionalString2(option.kind) || !isOptionalString2(option.name)) {
      return null;
    }
    parsedOptions.push({
      optionId: option.optionId,
      ...option.kind !== void 0 ? { kind: option.kind } : {},
      ...option.name !== void 0 ? { name: option.name } : {}
    });
  }
  return {
    id,
    workspaceId,
    sessionId,
    ...taskId !== void 0 ? { taskId } : {},
    ...taskPath !== void 0 ? { taskPath } : {},
    ...role !== void 0 ? { role } : {},
    toolTitle,
    ...toolCallId !== void 0 ? { toolCallId } : {},
    options: parsedOptions,
    status,
    createdAt,
    expiresAt,
    ...resolvedAt !== void 0 ? { resolvedAt } : {},
    ...resolvedBy !== void 0 ? { resolvedBy } : {}
  };
}
var ToolApprovalStore = class {
  constructor(dataDir, options) {
    this.items = /* @__PURE__ */ new Map();
    this.waiters = /* @__PURE__ */ new Map();
    this.loaded = false;
    this.closed = false;
    this.shutdownPromise = null;
    /** Serialize mutations + persist (same pattern as SessionRegistry write chain). */
    this.chain = Promise.resolve();
    this.file = path17.join(dataDir, "tool-approvals.json");
    this.writeState = options?.writeState ?? writeJsonAtomic;
  }
  enqueue(fn) {
    const run = this.chain.then(fn, fn);
    this.chain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async ensureLoaded() {
    if (this.loaded) return;
    return this.enqueue(async () => {
      if (this.loaded) return;
      try {
        const raw = await fs18.readFile(this.file, "utf8");
        let parsed;
        try {
          parsed = JSON.parse(raw);
        } catch {
          await this.quarantineCorrupt();
          this.loaded = true;
          return;
        }
        if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
          await this.quarantineCorrupt();
          this.loaded = true;
          return;
        }
        const items = parsed.items;
        if (items !== void 0 && !Array.isArray(items)) {
          await this.quarantineCorrupt();
          this.loaded = true;
          return;
        }
        const loaded = /* @__PURE__ */ new Map();
        const recoveredAt = (/* @__PURE__ */ new Date()).toISOString();
        let recoveredPending = false;
        for (const item of items ?? []) {
          const restored = parseApproval(item);
          if (!restored) {
            await this.quarantineCorrupt();
            this.loaded = true;
            return;
          }
          if (restored.status === "pending") {
            restored.status = "expired";
            restored.resolvedAt = recoveredAt;
            restored.resolvedBy = "service-restart";
            recoveredPending = true;
          }
          loaded.set(restored.id, restored);
        }
        if (recoveredPending) await this.persistSnapshot(loaded);
        this.items = loaded;
        this.loaded = true;
      } catch (err) {
        if (isNotFoundError(err)) {
          this.loaded = true;
          return;
        }
        throw err;
      }
    });
  }
  async listPending(workspaceId) {
    await this.ensureLoaded();
    await this.expireStale();
    return [...this.items.values()].filter(
      (i) => i.status === "pending" && (!workspaceId || i.workspaceId === workspaceId)
    ).map(cloneApproval);
  }
  async get(id) {
    await this.ensureLoaded();
    await this.expireStale(id);
    const item = this.items.get(id);
    return item ? cloneApproval(item) : void 0;
  }
  /**
   * Session-level wait barrier for concurrent ACP permission requests.
   * Serialized with add/resolve/expire so callers never resume a session from
   * a stale snapshot while another request for that session is still pending.
   */
  async hasPendingForSession(sessionId) {
    await this.ensureLoaded();
    return this.enqueue(async () => {
      await this.expireStaleUnlocked();
      return [...this.items.values()].some(
        (item) => item.sessionId === sessionId && item.status === "pending"
      );
    });
  }
  async add(item) {
    if (this.closed) throw new Error("Tool approval store is closed");
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("Tool approval store is closed");
      const stored = cloneApproval(item);
      const next = new Map(this.items);
      next.set(stored.id, stored);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneApproval(stored);
    });
  }
  /**
   * User-only resolve. Agent callers must not reach this via RPC auth (handlers enforce).
   * approve → allow_once at ACP layer; deny → cancelled.
   * Late approve after expire/deny/cancel fails (status !== pending).
   */
  async resolve(id, decision, resolvedBy) {
    if (this.closed) throw new Error("Tool approval store is closed");
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("Tool approval store is closed");
      await this.expireStaleUnlocked(id);
      const item = this.items.get(id);
      if (!item) throw new Error(`Tool approval not found: ${id}`);
      if (item.status !== "pending") {
        throw new Error(`Tool approval already ${item.status}: ${id}`);
      }
      const resolved = {
        ...item,
        status: decision,
        resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
        resolvedBy
      };
      const next = new Map(this.items);
      next.set(id, resolved);
      await this.persistSnapshot(next);
      this.items = next;
      this.notifyWaiters(id, decision);
      return cloneApproval(resolved);
    });
  }
  /**
   * Wait until user resolves or store-authoritative expiry. Returns approved | denied | expired.
   * Used by adapter onPermissionAsk bridge (service-owned).
   * timeoutMs bounds the wait; expireOne mutates the same record so late approve fails.
   */
  waitForDecision(id, timeoutMs) {
    if (this.closed) return Promise.resolve("denied");
    return new Promise((resolve15) => {
      let settled = false;
      let timer;
      const finish = (status) => {
        if (settled) return;
        settled = true;
        clearTimeout(timer);
        const list3 = this.waiters.get(id);
        if (list3) {
          this.waiters.set(
            id,
            list3.filter((w) => w.resolve !== finish)
          );
          if ((this.waiters.get(id) ?? []).length === 0) this.waiters.delete(id);
        }
        resolve15(status);
      };
      const list2 = this.waiters.get(id) ?? [];
      list2.push({ resolve: finish });
      this.waiters.set(id, list2);
      void this.get(id).then((item) => {
        if (settled) return;
        if (!item) {
          finish("expired");
          return;
        }
        if (item.status === "approved" || item.status === "denied") {
          finish(item.status);
          return;
        }
        if (item.status === "expired") {
          finish("expired");
          return;
        }
      }).catch(() => {
      });
      timer = setTimeout(() => {
        void this.expireOne(id).then((status) => {
          if (status === "approved" || status === "denied") {
            finish(status);
            return;
          }
          finish("expired");
        }).catch(() => {
          finish("expired");
        });
      }, Math.max(1, timeoutMs));
    });
  }
  /**
   * Stop accepting new tool asks and fail-close every live waiter.
   * Persistence is attempted first; if it fails, waiters are still denied so
   * service shutdown cannot be held open by permission deadlines. The next
   * service boot expires any orphaned pending disk rows.
   */
  shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    this.closed = true;
    this.shutdownPromise = this.shutdownInternal();
    return this.shutdownPromise;
  }
  async shutdownInternal() {
    try {
      await this.ensureLoaded();
      await this.enqueue(async () => {
        const next = new Map(this.items);
        const deniedIds = [];
        const resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
        for (const item of this.items.values()) {
          if (item.status !== "pending") continue;
          next.set(item.id, {
            ...item,
            status: "denied",
            resolvedAt,
            resolvedBy: "service-shutdown"
          });
          deniedIds.push(item.id);
        }
        if (deniedIds.length > 0) {
          await this.persistSnapshot(next);
          this.items = next;
          for (const id of deniedIds) this.notifyWaiters(id, "denied");
        }
      });
    } finally {
      this.notifyAllWaiters("denied");
    }
  }
  /** Cancel all pending for a session (session stop / fail). */
  async cancelSession(sessionId, reason = "denied") {
    await this.ensureLoaded();
    return this.enqueue(async () => {
      const next = new Map(this.items);
      const resolvedIds = [];
      for (const item of this.items.values()) {
        if (item.sessionId !== sessionId || item.status !== "pending") continue;
        next.set(item.id, {
          ...item,
          status: reason,
          resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
          resolvedBy: "service"
        });
        resolvedIds.push(item.id);
      }
      if (resolvedIds.length > 0) {
        await this.persistSnapshot(next);
        this.items = next;
        for (const id of resolvedIds) {
          this.notifyWaiters(id, reason === "expired" ? "expired" : "denied");
        }
      }
    });
  }
  /**
   * Force-expire one pending item (store timeout authority).
   * Idempotent: returns current terminal status if already resolved.
   */
  async expireOne(id) {
    await this.ensureLoaded();
    return this.enqueue(async () => {
      const item = this.items.get(id);
      if (!item) return "expired";
      if (item.status !== "pending") return item.status;
      const expired = {
        ...item,
        status: "expired",
        resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
        resolvedBy: "timeout"
      };
      const next = new Map(this.items);
      next.set(id, expired);
      await this.persistSnapshot(next);
      this.items = next;
      this.notifyWaiters(id, "expired");
      return "expired";
    });
  }
  notifyWaiters(id, status) {
    const list2 = this.waiters.get(id);
    if (!list2?.length) return;
    this.waiters.delete(id);
    for (const w of list2) w.resolve(status);
  }
  notifyAllWaiters(status) {
    const ids = [...this.waiters.keys()];
    for (const id of ids) this.notifyWaiters(id, status);
  }
  async expireStale(onlyId) {
    return this.enqueue(async () => {
      await this.expireStaleUnlocked(onlyId);
    });
  }
  async expireStaleUnlocked(onlyId) {
    const now = Date.now();
    const next = new Map(this.items);
    const expiredIds = [];
    for (const item of this.items.values()) {
      if (onlyId && item.id !== onlyId) continue;
      if (item.status !== "pending") continue;
      const exp = Date.parse(item.expiresAt);
      if (!Number.isFinite(exp) || exp > now) continue;
      next.set(item.id, {
        ...item,
        status: "expired",
        resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
        resolvedBy: "timeout"
      });
      expiredIds.push(item.id);
    }
    if (expiredIds.length > 0) {
      await this.persistSnapshot(next);
      this.items = next;
      for (const id of expiredIds) this.notifyWaiters(id, "expired");
    }
  }
  /**
   * Atomic temp-file + rename so a crashed mid-write cannot leave a partial file,
   * and concurrent readers never observe a torn document. Call only under enqueue.
   */
  async persistSnapshot(snapshot) {
    const items = [...snapshot.values()];
    const pending = items.filter((i) => i.status === "pending");
    const terminal = items.filter((i) => i.status !== "pending").sort((a, b) => (b.resolvedAt || "").localeCompare(a.resolvedAt || "")).slice(0, 50);
    await this.writeState(this.file, { items: [...pending, ...terminal] });
  }
  async quarantineCorrupt() {
    const backupPath = await backupCorruptMachineFile(this.file);
    warnCorruptMachineState(this.file, backupPath, "reset");
    this.items.clear();
  }
};
function makeToolApprovalId(rand = Math.random) {
  const alphabet = "0123456789abcdefghjkmnpqrstvwxyz";
  let s = "ta-";
  for (let i = 0; i < 10; i++) s += alphabet[Math.floor(rand() * alphabet.length)];
  return s;
}

// src/service/managed-result-report-draft-store.ts
import * as fs19 from "node:fs/promises";
import * as path18 from "node:path";
function cloneDraft(item) {
  return { ...item };
}
function isRecord12(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function isRequiredString4(value) {
  return typeof value === "string" && value.trim().length > 0;
}
function isOptionalString3(value) {
  return value === void 0 || typeof value === "string";
}
function isValidDate5(value) {
  return Number.isFinite(Date.parse(value));
}
function draftKey(workspaceId, taskPath) {
  return `${workspaceId}::${taskPath}`;
}
function parseDraft(value) {
  if (!isRecord12(value)) throw invalidDraftState();
  const keys = Object.keys(value);
  const allowed = /* @__PURE__ */ new Set([
    "workspaceId",
    "taskPath",
    "taskId",
    "sessionId",
    "assistantText",
    "createdAt",
    "updatedAt",
    "lastError"
  ]);
  if (keys.some((key2) => !allowed.has(key2))) throw invalidDraftState();
  const {
    workspaceId,
    taskPath,
    taskId,
    sessionId,
    assistantText,
    createdAt,
    updatedAt,
    lastError
  } = value;
  if (!isRequiredString4(workspaceId) || !isRequiredString4(taskPath) || !isRequiredString4(sessionId) || !isRequiredString4(assistantText) || !isRequiredString4(createdAt) || !isRequiredString4(updatedAt) || !isValidDate5(createdAt) || !isValidDate5(updatedAt) || !isOptionalString3(taskId) || !isOptionalString3(lastError)) {
    throw invalidDraftState();
  }
  const text3 = assistantText.trim();
  if (!text3) throw invalidDraftState();
  return {
    workspaceId: workspaceId.trim(),
    taskPath: taskPath.trim(),
    ...taskId !== void 0 && taskId.trim() ? { taskId: taskId.trim() } : {},
    sessionId: sessionId.trim(),
    assistantText: text3,
    createdAt,
    updatedAt,
    ...typeof lastError === "string" && lastError.trim() ? { lastError: lastError.trim() } : {}
  };
}
function invalidDraftState() {
  return new Error("Managed TaskResult report draft state is malformed");
}
var ManagedTaskResultReportDraftStore = class {
  constructor(dataDir, options) {
    /** Key = workspaceId::taskPath */
    this.items = /* @__PURE__ */ new Map();
    this.loaded = false;
    this.closed = false;
    this.shutdownPromise = null;
    this.chain = Promise.resolve();
    this.file = path18.join(dataDir, "managed-result-report-drafts.json");
    this.writeState = options?.writeState ?? writeJsonAtomic;
  }
  /** Absolute path of the durable JSON file (tests / diagnostics). */
  get filePath() {
    return this.file;
  }
  enqueue(fn) {
    const run = this.chain.then(fn, fn);
    this.chain = run.then(
      () => void 0,
      () => void 0
    );
    return run;
  }
  async ensureLoaded() {
    if (this.loaded) return;
    return this.enqueue(async () => {
      if (this.loaded) return;
      try {
        const raw = await fs19.readFile(this.file, "utf8");
        let parsed;
        try {
          parsed = JSON.parse(raw);
        } catch {
          throw invalidDraftState();
        }
        if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
          throw invalidDraftState();
        }
        if (Object.keys(parsed).some((key2) => key2 !== "items")) {
          throw invalidDraftState();
        }
        const items = parsed.items;
        if (!Array.isArray(items)) {
          throw invalidDraftState();
        }
        const loaded = /* @__PURE__ */ new Map();
        for (const item of items) {
          const restored = parseDraft(item);
          const key2 = draftKey(restored.workspaceId, restored.taskPath);
          if (loaded.has(key2)) throw invalidDraftState();
          loaded.set(key2, restored);
        }
        this.items = loaded;
        this.loaded = true;
      } catch (err) {
        if (isNotFoundError(err)) {
          this.loaded = true;
          return;
        }
        throw err;
      }
    });
  }
  /**
   * Lookup open draft for a task. Survives restart; never invents content.
   */
  async get(workspaceId, taskPath) {
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "ManagedTaskResultReportDraft.get requires workspaceId and taskPath"
      );
    }
    await this.ensureLoaded();
    const item = this.items.get(draftKey(workspaceId, taskPath));
    return item ? cloneDraft(item) : void 0;
  }
  /**
   * List open drafts (optional workspace filter). Operational diagnostics only.
   */
  async list(workspaceId) {
    await this.ensureLoaded();
    return [...this.items.values()].filter((i) => !workspaceId || i.workspaceId === workspaceId).map(cloneDraft);
  }
  /**
   * Preserve / refresh the final report before publish.
   * Idempotent for the same task: replaces assistantText/Session, clears the
   * prior diagnostic, and keeps createdAt on first write.
   */
  async preserve(input) {
    if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
    const workspaceId = input.workspaceId?.trim();
    const taskPath = input.taskPath?.trim();
    const sessionId = input.sessionId?.trim();
    const assistantText = input.assistantText?.trim();
    if (!workspaceId || !taskPath || !sessionId || !assistantText) {
      throw new Error(
        "ManagedTaskResultReportDraft.preserve requires workspaceId, taskPath, sessionId, and non-empty assistantText"
      );
    }
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
      const key2 = draftKey(workspaceId, taskPath);
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const existing = this.items.get(key2);
      const taskId = input.taskId?.trim();
      const nextRow = existing ? {
        ...existing,
        sessionId,
        assistantText,
        updatedAt: now,
        ...taskId ? { taskId } : existing.taskId ? { taskId: existing.taskId } : {}
      } : {
        workspaceId,
        taskPath,
        ...taskId ? { taskId } : {},
        sessionId,
        assistantText,
        createdAt: now,
        updatedAt: now
      };
      delete nextRow.lastError;
      const next = new Map(this.items);
      next.set(key2, nextRow);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneDraft(nextRow);
    });
  }
  /**
   * Annotate last publish failure without dropping the report body.
   * No-op (returns undefined) when no draft exists for the task.
   */
  async markFailed(workspaceId, taskPath, error) {
    if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "ManagedTaskResultReportDraft.markFailed requires workspaceId and taskPath"
      );
    }
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
      const key2 = draftKey(workspaceId, taskPath);
      const item = this.items.get(key2);
      if (!item) return void 0;
      const now = (/* @__PURE__ */ new Date()).toISOString();
      const message2 = (error || "managed auto-submit failed").trim() || "managed auto-submit failed";
      const nextRow = {
        ...item,
        updatedAt: now,
        lastError: message2
      };
      const next = new Map(this.items);
      next.set(key2, nextRow);
      await this.persistSnapshot(next);
      this.items = next;
      return cloneDraft(nextRow);
    });
  }
  /**
   * Remove draft after successful TaskResult or durable Task return projection.
   * Idempotent when already absent.
   */
  async clear(workspaceId, taskPath) {
    if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
    if (!workspaceId?.trim() || !taskPath?.trim()) {
      throw new Error(
        "ManagedTaskResultReportDraft.clear requires workspaceId and taskPath"
      );
    }
    await this.ensureLoaded();
    return this.enqueue(async () => {
      if (this.closed) throw new Error("ManagedTaskResultReportDraft store is closed");
      const key2 = draftKey(workspaceId, taskPath);
      if (!this.items.has(key2)) return false;
      const next = new Map(this.items);
      next.delete(key2);
      await this.persistSnapshot(next);
      this.items = next;
      return true;
    });
  }
  shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    this.closed = true;
    this.shutdownPromise = this.ensureLoaded().then(() => void 0);
    return this.shutdownPromise;
  }
  async persistSnapshot(snapshot) {
    const items = [...snapshot.values()];
    await this.writeState(this.file, { items });
  }
};

// src/service/connection-catalog.ts
var unwrap = (result) => {
  if (!result.ok) throw new RpcError(-32602, result.message);
  return result.value;
};
function rejectUnknownOrSecret(raw, allowed) {
  const allowedSet = new Set(allowed);
  for (const key2 of Object.keys(raw)) {
    if (allowedSet.has(key2)) continue;
    const lower = key2.toLowerCase();
    if (SECRET_CONNECTION_FIELD_HINTS.some((hint) => lower.includes(hint))) {
      throw new RpcError(-32602, `Rejected dangerous or unsupported Agent Connection field: ${key2}`);
    }
    throw new RpcError(-32602, `Unknown Agent Connection field: ${key2}`);
  }
}
var optional = (raw, key2, parse2) => !(key2 in raw) || raw[key2] === void 0 || raw[key2] === null ? void 0 : unwrap(parse2(raw[key2]));
function parseArgsValue2(value) {
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? { ok: true, value: [...value] } : { ok: false, message: "Invalid args: must be an array of strings" };
}
function parseCreate(raw) {
  rejectUnknownOrSecret(raw, AGENT_CONNECTION_CREATE_FIELDS);
  const connectionId = unwrap(parseConnectionIdValue(raw.connectionId));
  const provider = unwrap(parseNonEmptyStringValue(raw.provider, "provider"));
  const adapterId = unwrap(parseNonEmptyStringValue(raw.adapterId, "adapterId"));
  const connection = { connectionId, provider, adapterId };
  const assign = (key2, value) => {
    if (value !== void 0) connection[key2] = value;
  };
  assign("displayName", optional(raw, "displayName", (v) => parseNonEmptyStringValue(v, "displayName")));
  assign("command", optional(raw, "command", (v) => parseNonEmptyStringValue(v, "command")));
  assign("args", optional(raw, "args", parseArgsValue2));
  assign("model", optional(raw, "model", (v) => parseNonEmptyStringValue(v, "model")));
  assign("envKey", optional(raw, "envKey", (v) => parseEnvKeyValue(v, "envKey")));
  assign("launchSecretRef", optional(raw, "launchSecretRef", parseLaunchSecretRefValue));
  assign("endpoint", optional(raw, "endpoint", parseEndpointValue));
  assign("permissionPolicy", optional(raw, "permissionPolicy", parsePermissionPolicyValue));
  assign("promptTimeoutMs", optional(raw, "promptTimeoutMs", (v) => parsePositiveTimeoutValue(v, "promptTimeoutMs")));
  assign("permissionTimeoutMs", optional(raw, "permissionTimeoutMs", (v) => parsePositiveTimeoutValue(v, "permissionTimeoutMs")));
  if (raw.skills !== void 0 && raw.skills !== null) connection.skills = unwrap(parseSkillsArrayValue(raw.skills, defaultAllowedSkillRoots()));
  if (raw.mcpServers !== void 0 && raw.mcpServers !== null) connection.mcpServers = unwrap(parseMcpServersArrayValue(raw.mcpServers));
  try {
    return materializeAgentConnectionLaunch(connection);
  } catch (error) {
    throw new RpcError(-32602, error instanceof Error ? error.message : "Invalid Connection launch");
  }
}
function applyPatch(current, raw) {
  rejectUnknownOrSecret(raw, AGENT_CONNECTION_UPDATE_FIELDS);
  if ("connectionId" in raw || "provider" in raw || "adapterId" in raw) {
    throw new RpcError(-32602, "connectionId, provider, and adapterId are immutable; create a replacement Agent Connection instead");
  }
  const next = cloneAgentConnection(current);
  const scalar = [
    ["displayName", (v) => parseNonEmptyStringValue(v, "displayName")],
    ["model", (v) => parseNonEmptyStringValue(v, "model")],
    ["envKey", (v) => parseEnvKeyValue(v, "envKey")],
    ["launchSecretRef", parseLaunchSecretRefValue],
    ["permissionPolicy", parsePermissionPolicyValue],
    ["promptTimeoutMs", (v) => parsePositiveTimeoutValue(v, "promptTimeoutMs")],
    ["permissionTimeoutMs", (v) => parsePositiveTimeoutValue(v, "permissionTimeoutMs")]
  ];
  for (const [key2, parse2] of scalar) {
    if (!(key2 in raw) || raw[key2] === void 0) continue;
    if (raw[key2] === null) delete next[key2];
    else next[key2] = unwrap(parse2(raw[key2]));
  }
  if ("command" in raw && raw.command !== void 0) {
    if (raw.command === null) delete next.command;
    else next.command = unwrap(parseNonEmptyStringValue(raw.command, "command"));
  }
  if ("args" in raw && raw.args !== void 0) {
    if (raw.args === null) delete next.args;
    else next.args = unwrap(parseArgsValue2(raw.args));
  }
  if ("skills" in raw && raw.skills !== void 0) {
    if (raw.skills === null) delete next.skills;
    else next.skills = unwrap(parseSkillsArrayValue(raw.skills, defaultAllowedSkillRoots()));
  }
  if ("mcpServers" in raw && raw.mcpServers !== void 0) {
    if (raw.mcpServers === null) delete next.mcpServers;
    else next.mcpServers = unwrap(parseMcpServersArrayValue(raw.mcpServers));
  }
  if ("endpoint" in raw && raw.endpoint !== void 0) {
    const endpoint = raw.endpoint === null ? void 0 : unwrap(parseEndpointValue(raw.endpoint));
    return withAgentConnectionEndpoint(next, endpoint);
  }
  return next;
}
var AgentConnectionCatalog = class {
  constructor(dataDir, initial, options) {
    this.dataDir = dataDir;
    this.chain = Promise.resolve();
    this.connections = initial.map(cloneAgentConnection);
    this.persistToDisk = options?.persistToDisk !== false;
    this.saveConnections = options?.saveConnections ?? saveAgentConnections;
    this.publishConnections = options?.publishConnections;
  }
  enqueue(operation) {
    const run = this.chain.then(operation, operation);
    this.chain = run.then(() => void 0, () => void 0);
    return run;
  }
  list() {
    return this.connections.map(cloneAgentConnection);
  }
  get(connectionId) {
    const connection = this.connections.find((candidate) => candidate.connectionId === connectionId);
    return connection && cloneAgentConnection(connection);
  }
  async commit(next) {
    const canonical = next.map(cloneAgentConnection);
    if (this.persistToDisk) await this.saveConnections(this.dataDir, canonical);
    await this.publishConnections?.(canonical.map(cloneAgentConnection));
    this.connections = canonical;
  }
  async create(raw) {
    return this.enqueue(async () => {
      const connection = parseCreate(raw);
      if (this.connections.some((candidate) => candidate.connectionId === connection.connectionId)) throw new RpcError(-32009, `Agent Connection already exists: ${connection.connectionId}`);
      await this.commit([...this.connections, connection]);
      return this.get(connection.connectionId);
    });
  }
  async update(connectionIdRaw, raw) {
    return this.enqueue(async () => {
      const connectionId = unwrap(parseConnectionIdValue(connectionIdRaw));
      const index2 = this.connections.findIndex((connection) => connection.connectionId === connectionId);
      if (index2 < 0) throw new RpcError(-32004, `Agent Connection not found: ${connectionId}`);
      const next = applyPatch(this.connections[index2], raw);
      await this.commit(this.connections.map((connection, i) => i === index2 ? next : connection));
      return this.get(connectionId);
    });
  }
  async delete(connectionIdRaw) {
    return this.enqueue(async () => {
      const connectionId = unwrap(parseConnectionIdValue(connectionIdRaw));
      if (!this.connections.some((connection) => connection.connectionId === connectionId)) throw new RpcError(-32004, `Agent Connection not found: ${connectionId}`);
      await this.commit(this.connections.filter((connection) => connection.connectionId !== connectionId));
      return { deleted: connectionId };
    });
  }
};

// src/runtime/agent-runtime.ts
import * as path19 from "node:path";
import { createHash as createHash6, randomBytes as randomBytes3 } from "node:crypto";

// src/runtime/process-supervisor.ts
import { spawn as spawn5 } from "node:child_process";
var ProcessSupervisor = class {
  constructor(options = {}) {
    this.children = /* @__PURE__ */ new Map();
    this.gracefulMs = options.gracefulMs ?? 2e3;
    this.forceExitMs = options.forceExitMs ?? 1500;
    this.stdoutRingBytes = options.stdoutRingBytes ?? 0;
    this.onExit = options.onExit;
    this.onStdout = options.onStdout;
  }
  listLive() {
    return [...this.children.entries()].filter(([, c]) => !c.exited).map(([id]) => id);
  }
  get(sessionId) {
    const live = this.children.get(sessionId);
    if (!live) return null;
    return {
      sessionId,
      pid: live.child.pid ?? -1,
      startedAt: live.startedAt,
      exitCode: live.exitCode,
      signal: live.signal,
      exited: live.exited
    };
  }
  isAlive(sessionId) {
    const live = this.children.get(sessionId);
    if (!live || live.exited) return false;
    const pid = live.child.pid;
    if (pid == null || pid <= 0) return false;
    try {
      process.kill(pid, 0);
      return true;
    } catch {
      return false;
    }
  }
  async start(sessionId, launch) {
    if (this.children.has(sessionId) && this.isAlive(sessionId)) {
      throw new Error(`Process already live for session ${sessionId}`);
    }
    const env = buildManagedChildEnv({
      launchEnv: launch.env,
      reserved: launch.coreEnv
    });
    const coreSecrets = collectSecretValues(launch.coreEnv);
    const secretValues = collectSecretValues(launch.env, [
      ...launch.diagnosticSecrets ?? [],
      ...coreSecrets
    ]);
    const stdoutDiagnostic = new BoundedDiagnosticRedactor(
      secretValues,
      ACP_DIAGNOSTIC_EVENT_BYTES
    );
    const stderrDiagnostic = new BoundedDiagnosticRedactor(
      secretValues,
      ACP_DIAGNOSTIC_EVENT_BYTES
    );
    const child = spawn5(launch.command, launch.args, {
      cwd: launch.cwd,
      env,
      stdio: ["ignore", "pipe", "pipe"],
      windowsHide: true,
      // Detached false so service shutdown can reap children as a group.
      detached: false
    });
    const live = {
      sessionId,
      child,
      startedAt: Date.now(),
      exitCode: null,
      signal: null,
      exited: false,
      stdoutBuf: ""
    };
    this.children.set(sessionId, live);
    let spawned = false;
    let exitNotified = false;
    const appendRing = (text3) => {
      if (this.stdoutRingBytes <= 0) return;
      live.stdoutBuf = appendUtf8Tail(
        live.stdoutBuf,
        text3,
        this.stdoutRingBytes
      );
    };
    const emitDiagnostic = (text3) => {
      if (!text3) return;
      appendRing(text3);
      this.onStdout?.(sessionId, text3);
    };
    child.stdout?.on("data", (chunk) => {
      emitDiagnostic(stdoutDiagnostic.pushBuffer(chunk));
    });
    child.stderr?.on("data", (chunk) => {
      emitDiagnostic(stderrDiagnostic.pushBuffer(chunk));
    });
    child.stdout?.on("end", () => emitDiagnostic(stdoutDiagnostic.flush()));
    child.stderr?.on("end", () => emitDiagnostic(stderrDiagnostic.flush()));
    const notifyExit = () => {
      if (!spawned || exitNotified) return;
      exitNotified = true;
      this.onExit?.({
        sessionId,
        exitCode: live.exitCode,
        signal: live.signal ?? void 0
      });
    };
    child.on("exit", (code, signal) => {
      live.exited = true;
      live.exitCode = code;
      live.signal = signal;
      if (live.killTimer) {
        clearTimeout(live.killTimer);
        live.killTimer = void 0;
      }
      notifyExit();
    });
    child.on("error", () => {
      if (!live.exited) {
        live.exited = true;
        live.exitCode = null;
        live.signal = "error";
      }
      notifyExit();
    });
    try {
      await new Promise((resolve15, reject) => {
        const onSpawn = () => {
          child.off("error", onStartError);
          spawned = true;
          resolve15();
        };
        const onStartError = (error) => {
          child.off("spawn", onSpawn);
          reject(error);
        };
        child.once("spawn", onSpawn);
        child.once("error", onStartError);
      });
    } catch (error) {
      this.children.delete(sessionId);
      const message2 = error instanceof Error ? error.message : String(error);
      throw new Error(`Failed to spawn process for session ${sessionId}: ${message2}`);
    }
    if (child.pid == null) {
      this.children.delete(sessionId);
      throw new Error(`Failed to spawn process for session ${sessionId}: missing pid`);
    }
    return {
      sessionId,
      pid: child.pid,
      startedAt: live.startedAt,
      exitCode: live.exitCode,
      signal: live.signal,
      exited: live.exited
    };
  }
  /**
   * Graceful stop → timeout → force kill (Windows: taskkill tree).
   */
  async stop(sessionId, options) {
    const live = this.children.get(sessionId);
    if (!live || live.exited) {
      this.children.delete(sessionId);
      return;
    }
    const gracefulMs = options?.gracefulMs ?? this.gracefulMs;
    const signal = options?.signal ?? "SIGTERM";
    try {
      live.child.kill(signal);
    } catch {
    }
    if (live.exited) {
      this.children.delete(sessionId);
      return;
    }
    const exitedGracefully = await this.waitForExit(live, gracefulMs);
    if (!exitedGracefully) {
      await this.forceKill(live);
      const exitedAfterForce = await this.waitForExit(live, this.forceExitMs);
      if (!exitedAfterForce || !live.exited) {
        throw new Error(
          `Stop failed for session ${sessionId}: child exit was not confirmed`
        );
      }
    }
    this.children.delete(sessionId);
  }
  async waitForExit(live, timeoutMs) {
    if (live.exited) return true;
    return new Promise((resolve15) => {
      let settled = false;
      const done = () => {
        if (settled) return;
        settled = true;
        if (live.killTimer) {
          clearTimeout(live.killTimer);
          live.killTimer = void 0;
        }
        live.child.removeListener("exit", onExit);
        resolve15(live.exited);
      };
      const onExit = () => done();
      live.child.once("exit", onExit);
      live.killTimer = setTimeout(() => {
        done();
      }, timeoutMs);
    });
  }
  /** Stop every live child (service shutdown default for push-mode). */
  async stopAll(reason = "shutdown") {
    void reason;
    const ids = this.listLive();
    await Promise.all(ids.map((id) => this.stop(id)));
  }
  getStdoutTail(sessionId) {
    return this.children.get(sessionId)?.stdoutBuf ?? "";
  }
  async forceKill(live) {
    if (live.exited) return;
    const pid = live.child.pid;
    if (pid == null) return;
    if (process.platform === "win32") {
      await new Promise((resolve15) => {
        const killer = spawn5("taskkill", ["/pid", String(pid), "/T", "/F"], {
          windowsHide: true,
          stdio: "ignore"
        });
        killer.on("exit", () => resolve15());
        killer.on("error", () => resolve15());
        setTimeout(resolve15, 1500);
      });
    } else {
      try {
        live.child.kill("SIGKILL");
      } catch {
      }
    }
  }
};

// src/runtime/agent-runtime.ts
function handleFrom(record) {
  return {
    sessionId: record.id,
    connectionId: record.connectionId,
    adapterId: record.adapterId,
    state: record.state,
    pid: record.pid,
    roleId: record.roleId,
    runtimeWorkspace: record.runtimeWorkspace,
    ...record.providerContextRestored !== void 0 ? { providerContextRestored: record.providerContextRestored } : {},
    createdAt: record.createdAt,
    updatedAt: record.updatedAt
  };
}
function boundRuntimeDiagnosticEvent(ev) {
  if (ev.type === "session.stdout_tail") {
    return {
      ...ev,
      text: truncateUtf8Text(ev.text, ACP_DIAGNOSTIC_EVENT_BYTES)
    };
  }
  if (ev.type === "session.failed") {
    return {
      ...ev,
      error: truncateUtf8Text(ev.error, ACP_DIAGNOSTIC_EVENT_BYTES)
    };
  }
  if (ev.type === "session.waiting_user") {
    return {
      ...ev,
      summary: truncateUtf8Text(ev.summary, ACP_DIAGNOSTIC_EVENT_BYTES)
    };
  }
  if (ev.type === "session.acp_observation") {
    return {
      ...ev,
      observation: boundAcpRuntimeObservation(ev.observation)
    };
  }
  return ev;
}
function boundAcpRuntimeObservation(observation) {
  const count = Number.isInteger(observation.permissionRequestCount) ? Math.max(
    0,
    Math.min(
      ACP_PERMISSION_REQUEST_COUNT_MAX,
      observation.permissionRequestCount
    )
  ) : 0;
  const policy = ["allow", "ask", "deny"].includes(
    observation.permissionPolicy
  ) ? observation.permissionPolicy : "deny";
  const exitCode = observation.exitCode === null ? null : Number.isInteger(observation.exitCode) ? Math.max(
    -2147483648,
    Math.min(2147483647, observation.exitCode)
  ) : void 0;
  return {
    permissionRequestCount: count,
    permissionPolicy: policy,
    ...observation.permissionDecision === "allow" || observation.permissionDecision === "deny" ? { permissionDecision: observation.permissionDecision } : {},
    ...observation.permissionOutcome === "allow_once" || observation.permissionOutcome === "cancelled" ? { permissionOutcome: observation.permissionOutcome } : {},
    ...observation.promptStopReason !== void 0 ? {
      promptStopReason: truncateUtf8Text(
        observation.promptStopReason,
        ACP_OBSERVATION_TEXT_BYTES
      )
    } : {},
    spontaneousChildExit: observation.spontaneousChildExit === true,
    ...exitCode !== void 0 ? { exitCode } : {},
    ...observation.signal !== void 0 ? {
      signal: truncateUtf8Text(
        observation.signal,
        ACP_OBSERVATION_SIGNAL_BYTES
      )
    } : {}
  };
}
function copyRuntimeErrorMetadata(error) {
  if (!error || typeof error !== "object") return {};
  const source = error;
  return {
    ...typeof source.code === "string" ? { code: source.code } : {},
    ...source.terminalAlreadyEmitted === true ? { terminalAlreadyEmitted: true } : {}
  };
}
var AgentRuntime = class {
  constructor(options) {
    this.connections = /* @__PURE__ */ new Map();
    this.adapters = /* @__PURE__ */ new Map();
    this.managed = /* @__PURE__ */ new Map();
    this.startInFlight = /* @__PURE__ */ new Map();
    this.resumeInFlight = /* @__PURE__ */ new Map();
    this.childExitInFlight = /* @__PURE__ */ new Map();
    this.managedTerminalInFlight = /* @__PURE__ */ new Map();
    this.sinks = /* @__PURE__ */ new Map();
    this.globalSinks = /* @__PURE__ */ new Set();
    /** Test-only: every sendFollowUpPrompt attempt (including not-alive / unsupported). */
    this.followUpAttemptsForTests = [];
    this.closing = false;
    this.closed = false;
    this.dataDir = path19.resolve(options.dataDir);
    this.sessionTokenKey = options.sessionTokenKey ?? randomBytes3(32).toString("base64url");
    this.registry = new SessionRegistry(this.dataDir);
    this.resolveConnectionEnv = options.resolveConnectionEnv;
    this.resolveLaunchSecretRef = options.resolveLaunchSecretRef;
    this.packageRoot = options.packageRoot;
    for (const connection of options.connections ?? []) {
      this.connections.set(connection.connectionId, cloneAgentConnection(connection));
    }
    const adapterList = options.adapters ?? [
      createFakeAdapter(),
      createGrokAcpAdapter(),
      createCodexAcpAdapter(),
      createClaudeAcpAdapter(),
      createOpenCodeAcpAdapter(),
      createCopilotAcpAdapter(),
      createPiAcpAdapter()
    ];
    for (const a of adapterList) {
      this.adapters.set(a.id, a);
    }
    if (!this.adapters.has(FAKE_ADAPTER_ID)) {
      this.adapters.set(FAKE_ADAPTER_ID, createFakeAdapter());
    }
    if (!this.adapters.has(GROK_ACP_ADAPTER_ID)) {
      this.adapters.set(GROK_ACP_ADAPTER_ID, createGrokAcpAdapter());
    }
    if (!this.adapters.has(CODEX_ACP_ADAPTER_ID)) {
      this.adapters.set(CODEX_ACP_ADAPTER_ID, createCodexAcpAdapter());
    }
    if (!this.adapters.has(CLAUDE_ACP_ADAPTER_ID)) {
      this.adapters.set(CLAUDE_ACP_ADAPTER_ID, createClaudeAcpAdapter());
    }
    if (!this.adapters.has(OPENCODE_ACP_ADAPTER_ID)) {
      this.adapters.set(OPENCODE_ACP_ADAPTER_ID, createOpenCodeAcpAdapter());
    }
    if (!this.adapters.has(COPILOT_ACP_ADAPTER_ID)) {
      this.adapters.set(COPILOT_ACP_ADAPTER_ID, createCopilotAcpAdapter());
    }
    if (!this.adapters.has(PI_ACP_ADAPTER_ID)) {
      this.adapters.set(PI_ACP_ADAPTER_ID, createPiAcpAdapter());
    }
    this.supervisor = new ProcessSupervisor({
      gracefulMs: options.gracefulMs ?? 2e3,
      stdoutRingBytes: options.captureStdout === false ? 0 : 4096,
      onExit: (info) => {
        const projection = this.onChildExit(info.sessionId, info.exitCode, info.signal);
        this.childExitInFlight.set(info.sessionId, projection);
        void projection.finally(() => {
          if (this.childExitInFlight.get(info.sessionId) === projection) {
            this.childExitInFlight.delete(info.sessionId);
          }
        }).catch(() => void 0);
      },
      onStdout: (sessionId, text3) => {
        if (options.captureStdout === false) return;
        this.emit({ type: "session.stdout_tail", sessionId, text: text3 });
      }
    });
  }
  registerConnection(connection) {
    this.connections.set(connection.connectionId, cloneAgentConnection(connection));
  }
  /**
   * Full replace of the in-memory Agent Connection catalog.
   * Does not touch live sessions — only new startSession sees the new map.
   * Stores clones so callers cannot mutate the map.
   */
  replaceConnectionCatalog(connections) {
    this.connections.clear();
    for (const connection of connections) {
      if (connection && typeof connection.connectionId === "string") {
        this.connections.set(connection.connectionId, cloneAgentConnection(connection));
      }
    }
  }
  /** Lookup a single machine-local Agent Connection clone. */
  getConnection(connectionId) {
    const connection = this.connections.get(connectionId);
    return connection ? cloneAgentConnection(connection) : void 0;
  }
  /** Immutable non-secret launch facts for a fresh Session. */
  snapshotConnectionForStart(connectionId) {
    const connection = this.connections.get(connectionId);
    if (!connection) throw new Error(`Unknown Agent Connection: ${connectionId}`);
    return createAgentConnectionSnapshot(connection, {
      effectiveEndpointDigest: this.effectiveEndpointDigest(connection)
    });
  }
  /** Machine-local catalog snapshot (cloned entries). */
  listConnections() {
    return [...this.connections.values()].map(cloneAgentConnection);
  }
  registerAdapter(adapter) {
    this.adapters.set(adapter.id, adapter);
  }
  /** Subscribe to all runtime events (service fan-out helper). */
  subscribeAll(sink) {
    this.globalSinks.add(sink);
    return () => this.globalSinks.delete(sink);
  }
  subscribe(sessionId, sink) {
    let set = this.sinks.get(sessionId);
    if (!set) {
      set = /* @__PURE__ */ new Set();
      this.sinks.set(sessionId, set);
    }
    set.add(sink);
    return () => {
      set.delete(sink);
      if (set.size === 0) this.sinks.delete(sessionId);
    };
  }
  async startSession(req) {
    this.assertOpen();
    if (!isSessionId2(req.sessionId)) {
      throw new Error(`Invalid session id: ${req.sessionId}`);
    }
    if (this.startInFlight.has(req.sessionId)) {
      throw new Error(`Session start already in progress: ${req.sessionId}`);
    }
    if (this.resumeInFlight.has(req.sessionId)) {
      throw new Error(`Session resume already in progress: ${req.sessionId}`);
    }
    const operation = this.startSessionExclusive(req);
    this.startInFlight.set(req.sessionId, operation);
    try {
      return await operation;
    } finally {
      if (this.startInFlight.get(req.sessionId) === operation) {
        this.startInFlight.delete(req.sessionId);
      }
    }
  }
  /**
   * Create the exact durable Session identity before its Task is written.
   * Connection selection happens once here; later start/resume consume only the
   * immutable snapshot on this record.
   */
  async reserveSession(req) {
    this.assertOpen();
    if (!isSessionId2(req.sessionId)) {
      throw new Error(`Invalid session id: ${req.sessionId}`);
    }
    const taskId = req.currentTaskId.trim();
    const workspace = req.workspace.trim();
    if (!taskId) throw new Error("reserveSession requires currentTaskId");
    if (!workspace) throw new Error("reserveSession requires workspace");
    const connection = this.connections.get(req.connectionId);
    if (!connection) {
      throw new Error(`Unknown Agent Connection: ${req.connectionId}`);
    }
    const adapter = this.adapters.get(connection.adapterId);
    if (!adapter) throw new Error(`Unknown adapter: ${connection.adapterId}`);
    if (!adapter.capabilities().canSpawn) {
      throw new Error(`Adapter ${adapter.id} cannot spawn (pull-host only)`);
    }
    const cwd = req.runtimeWorkspace?.cwd ?? req.cwd ?? req.workspaceLane?.worktree;
    if (!cwd) {
      throw new Error(
        "reserveSession requires runtimeWorkspace.cwd, cwd, or workspaceLane.worktree"
      );
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const record = {
      id: req.sessionId,
      connectionId: connection.connectionId,
      adapterId: adapter.id,
      connectionSnapshot: createAgentConnectionSnapshot(connection, {
        effectiveEndpointDigest: this.effectiveEndpointDigest(connection)
      }),
      state: "reserved",
      runtimeWorkspace: { cwd },
      workspace,
      workspaceLane: req.workspaceLane,
      createdAt: now,
      updatedAt: now,
      currentTaskId: taskId
    };
    await this.registry.create(record);
    return handleFrom(record);
  }
  /**
   * Register a pull-host / external GUI session without spawning ACP.
   * Idempotent: same sessionId or externalKey while state remains external reuses the row.
   */
  async enterExternalSession(req) {
    this.assertOpen();
    const externalKey = req.externalKey?.trim() || void 0;
    const roleId = req.roleId?.trim() || void 0;
    if (roleId && !isRoleId(roleId)) {
      throw new Error(`Invalid Role id: ${roleId}`);
    }
    const workspace = req.workspace?.trim() || void 0;
    const cwd = req.runtimeWorkspace?.cwd ?? req.cwd ?? req.workspaceLane?.worktree ?? void 0;
    if (req.sessionId) {
      if (!isSessionId2(req.sessionId)) {
        throw new Error(`Invalid session id: ${req.sessionId}`);
      }
      const existing = await this.registry.read(req.sessionId);
      if (existing) {
        if (existing.state === "external") {
          const patch = {};
          if (roleId && existing.roleId !== roleId) {
            throw new Error(
              `External Session Role binding mismatch: existing=${existing.roleId ?? "(none)"} requested=${roleId}`
            );
          }
          if (workspace && existing.workspace !== workspace) patch.workspace = workspace;
          if (cwd && existing.runtimeWorkspace?.cwd !== cwd) {
            patch.runtimeWorkspace = { cwd };
          }
          if (req.currentTaskId && existing.currentTaskId !== req.currentTaskId) {
            patch.currentTaskId = req.currentTaskId;
          }
          if (externalKey && existing.externalKey !== externalKey) {
            patch.externalKey = externalKey;
          }
          if (Object.keys(patch).length > 0) {
            const updated = await this.registry.update(req.sessionId, patch);
            return this.externalHandle(updated);
          }
          return this.externalHandle(existing);
        }
        if (SessionRegistry.isNonTerminal(existing.state)) {
          throw new Error(
            `Session already active as managed runtime (state=${existing.state}): ${req.sessionId}`
          );
        }
        if (existing.adapterId !== EXTERNAL_ADAPTER_ID) {
          throw new Error(
            `Terminal managed Session cannot be reopened as external: ${req.sessionId}`
          );
        }
        if (roleId && existing.roleId !== roleId) {
          throw new Error(
            `External Session Role binding mismatch: existing=${existing.roleId ?? "(none)"} requested=${roleId}`
          );
        }
        const reopened = await this.registry.update(req.sessionId, {
          state: "external",
          ...workspace ? { workspace } : {},
          ...cwd ? { runtimeWorkspace: { cwd } } : {},
          ...req.currentTaskId ? { currentTaskId: req.currentTaskId } : {},
          ...externalKey ? { externalKey } : {},
          pid: void 0,
          exitCode: void 0,
          lastError: void 0,
          stopReason: void 0
        });
        return this.externalHandle(reopened);
      }
    }
    if (externalKey || workspace && roleId) {
      const all2 = await this.registry.list();
      const match = all2.find((rec) => {
        if (rec.state !== "external") return false;
        if (workspace && rec.workspace && rec.workspace !== workspace) return false;
        if (externalKey) {
          return recordExternalKey(rec) === externalKey;
        }
        return Boolean(roleId && rec.roleId === roleId);
      });
      if (match) {
        const patch = {};
        if (req.currentTaskId && match.currentTaskId !== req.currentTaskId) {
          patch.currentTaskId = req.currentTaskId;
        }
        if (cwd && match.runtimeWorkspace?.cwd !== cwd) {
          patch.runtimeWorkspace = { cwd };
        }
        if (externalKey && match.externalKey !== externalKey) {
          patch.externalKey = externalKey;
        }
        if (Object.keys(patch).length > 0) {
          const updated = await this.registry.update(match.id, patch);
          return this.externalHandle(updated);
        }
        return this.externalHandle(match);
      }
    }
    const sessionId = req.sessionId && isSessionId2(req.sessionId) ? req.sessionId : makeSessionId();
    const now = (/* @__PURE__ */ new Date()).toISOString();
    const record = {
      id: sessionId,
      adapterId: EXTERNAL_ADAPTER_ID,
      roleId,
      state: "external",
      runtimeWorkspace: cwd ? { cwd } : void 0,
      workspace,
      workspaceLane: req.workspaceLane,
      createdAt: now,
      updatedAt: now,
      currentTaskId: req.currentTaskId,
      ...externalKey ? { externalKey } : {}
    };
    await this.registry.create(record);
    return this.externalHandle(record);
  }
  externalHandle(record) {
    return {
      ...handleFrom(record),
      sessionToken: deriveSessionToken(this.sessionTokenKey, record.id)
    };
  }
  async startSessionExclusive(req) {
    const record = await this.registry.read(req.sessionId);
    if (!record) throw new Error(`Reserved Session not found: ${req.sessionId}`);
    if (record.state !== "reserved") {
      throw new Error(`Session is not reserved for first start: ${req.sessionId} (${record.state})`);
    }
    const connection = this.connectionForResume(record);
    return this.startSessionWithConnection(req, record, connection);
  }
  async startSessionWithConnection(req, record, route) {
    const adapter = this.adapters.get(route.adapterId);
    if (!adapter) {
      throw new Error(`Unknown adapter: ${route.adapterId}`);
    }
    const caps = adapter.capabilities();
    if (!caps.canSpawn) {
      throw new Error(`Adapter ${adapter.id} cannot spawn (pull-host only)`);
    }
    const recordedCwd = record.runtimeWorkspace?.cwd ?? record.workspaceLane?.worktree;
    const requestedCwd = req.runtimeWorkspace?.cwd ?? req.cwd ?? req.workspaceLane?.worktree;
    if (recordedCwd && requestedCwd && !sameRuntimeCwd(recordedCwd, requestedCwd)) {
      throw new Error(
        `startSession cwd mismatch: recorded=${recordedCwd} requested=${requestedCwd}`
      );
    }
    const cwd = recordedCwd ?? requestedCwd;
    if (!cwd) {
      throw new Error("startSession requires runtimeWorkspace.cwd, cwd, or workspaceLane.worktree");
    }
    const starting = await this.registry.update(req.sessionId, {
      state: "starting",
      runtimeWorkspace: { cwd }
    });
    this.emit({ type: "session.starting", sessionId: req.sessionId });
    let startedManaged;
    let resolvedEnv = {};
    let diagnosticSecrets = [];
    try {
      resolvedEnv = await this.resolveLaunchSecretEnv(route);
      const coreEnv = {
        // Reserved routing authority: installed native Tent hooks spawned by an
        // isolated Service must attach back to that Service, never %APPDATA%\Tent.
        TENT_SERVICE_DATA_DIR: this.dataDir,
        TENT_SESSION_ID: req.sessionId,
        TENT_SESSION_TOKEN: deriveSessionToken(this.sessionTokenKey, req.sessionId)
      };
      const planEnv = {
        ...stripRouteRequestEnv(req.env),
        ...resolvedEnv,
        ...coreEnv
      };
      const acpLaunch = await this.buildAcpLaunchExtras(route, planEnv);
      diagnosticSecrets = Array.from(
        /* @__PURE__ */ new Set([
          ...Object.values(resolvedEnv).filter(
            (v) => typeof v === "string" && v.length > 0
          ),
          ...acpLaunch.diagnosticSecrets
        ])
      );
      const plan = {
        sessionId: req.sessionId,
        connectionId: starting.connectionId,
        cwd,
        env: planEnv,
        coreEnv,
        diagnosticSecrets,
        bootstrapPrompt: req.bootstrapPrompt,
        // Ephemeral path refs only — never base64; not written to SessionRecord.
        bootstrapImageRefs: req.bootstrapImageRefs,
        command: route.command,
        args: route.args,
        extras: {
          fake: route.fake,
          acp: this.acpOptionsForRoute(route),
          // Snapshot-time ACP projection (skills + mcp). Running sessions do not hot-reload.
          ...acpLaunch.extras,
          // System root for safe image byte reads at prompt time (ephemeral; not SessionRecord).
          ...req.bootstrapImageRefs && req.bootstrapImageRefs.length > 0 && typeof req.bootstrapImageSystemRoot === "string" && req.bootstrapImageSystemRoot.trim() ? { bootstrapImageSystemRoot: req.bootstrapImageSystemRoot.trim() } : {}
        }
      };
      let pid;
      let resumeToken;
      let startupCommitted = false;
      let startupLivePid;
      let terminalProjection;
      let terminalDuringManagedStart;
      if (typeof adapter.startManagedSession === "function") {
        let managed;
        try {
          managed = await adapter.startManagedSession(plan, (rawEvent) => {
            const ev = boundRuntimeDiagnosticEvent(rawEvent);
            const managedAtEvent = this.managed.get(req.sessionId) ?? startedManaged;
            const terminalWhileAlive = (ev.type === "session.failed" || ev.type === "session.exited") && managedAtEvent?.isAlive() === true;
            if (ev.type === "session.failed") {
              terminalDuringManagedStart = { state: "failed", error: ev.error };
              terminalProjection = this.trackManagedTerminal(
                req.sessionId,
                "failed",
                ev.error
              );
            } else if (ev.type === "session.exited") {
              terminalDuringManagedStart = {
                state: "stopped",
                exitCode: ev.exitCode
              };
              terminalProjection = this.trackManagedTerminal(
                req.sessionId,
                "stopped",
                void 0,
                ev.exitCode
              );
            } else if (ev.type === "session.waiting_user") {
              void this.registry.update(req.sessionId, { state: "waiting-user" }).catch(() => void 0);
            } else if (ev.type === "session.live") {
              if (!startupCommitted) {
                startupLivePid = ev.pid;
                return;
              }
              void this.registry.update(req.sessionId, {
                state: "live",
                ...ev.pid != null ? { pid: ev.pid } : {}
              }).catch(() => void 0);
            } else if (ev.type === "session.config_options") {
              void this.registry.update(req.sessionId, {
                acpSession: cloneAcpSessionConfigSnapshot(ev.sessionConfig)
              }).catch(() => void 0);
            } else if (ev.type === "session.acp_observation") {
              void this.registry.update(req.sessionId, {
                acpObservation: { ...ev.observation }
              }).catch(() => void 0);
            }
            if (!terminalWhileAlive) this.emit(ev);
          });
        } catch (error) {
          if (error instanceof ManagedSessionStartupError) {
            startedManaged = error.managedSession;
            this.managed.set(req.sessionId, startedManaged);
          }
          throw error;
        }
        startedManaged = managed;
        if (terminalDuringManagedStart) {
          const terminal = terminalDuringManagedStart;
          await (terminalProjection ?? this.trackManagedTerminal(
            req.sessionId,
            terminal.state,
            terminal.state === "failed" ? terminal.error : void 0,
            terminal.state === "stopped" ? terminal.exitCode : void 0
          ));
          throw Object.assign(
            new Error(
              terminal.state === "failed" ? terminal.error : `Managed session exited during startup (code=${terminal.exitCode})`
            ),
            { terminalAlreadyEmitted: true }
          );
        }
        this.managed.set(req.sessionId, managed);
        pid = managed.pid;
        if (managed.providerSessionId) {
          resumeToken = managed.providerSessionId;
        }
      } else {
        const launch = await adapter.resolveLaunch(plan);
        const proc = await this.supervisor.start(req.sessionId, {
          ...launch,
          coreEnv: {
            ...launch.coreEnv,
            ...coreEnv
          },
          diagnosticSecrets: [
            ...launch.diagnosticSecrets ?? [],
            ...diagnosticSecrets
          ]
        });
        pid = proc.pid;
        resumeToken = adapter.capabilities().canResume ? `resume:${req.sessionId}` : void 0;
      }
      const live = await this.registry.update(req.sessionId, {
        state: "live",
        pid,
        resumeToken,
        ...startedManaged?.acpSession ? {
          acpSession: cloneAcpSessionConfigSnapshot(
            startedManaged.acpSession
          )
        } : {},
        lastError: void 0,
        exitCode: void 0,
        stopReason: void 0
      });
      startupCommitted = true;
      this.emit({
        type: "session.live",
        sessionId: req.sessionId,
        pid: startupLivePid ?? pid
      });
      return handleFrom(live);
    } catch (err) {
      let retainedManaged = false;
      let stopError;
      if (startedManaged) {
        try {
          await startedManaged.stop("interrupt");
        } catch (error) {
          stopError = error;
        }
        retainedManaged = startedManaged.isAlive();
        if (retainedManaged) {
          this.managed.set(req.sessionId, startedManaged);
        } else {
          this.managed.delete(req.sessionId);
          await this.waitForManagedTerminal(req.sessionId, true);
        }
      } else if (this.supervisor.isAlive(req.sessionId)) {
        await this.supervisor.stop(req.sessionId).catch(() => void 0);
        await this.waitForChildExit(req.sessionId, true);
      }
      const rawMessage = retainedManaged ? `${err instanceof Error ? err.message : String(err)}; ${stopError instanceof Error ? stopError.message : "managed child exit was not confirmed"}` : err instanceof Error ? err.message : String(err);
      const message2 = redactDiagnosticText(rawMessage, {
        env: {
          ...req.env ?? {}
        },
        secrets: diagnosticSecrets
      });
      if (retainedManaged) {
        const retained = await this.registry.update(req.sessionId, {
          state: "starting",
          pid: startedManaged?.pid,
          lastError: message2
        });
        throw Object.assign(new Error(message2), {
          session: handleFrom(retained),
          ...copyRuntimeErrorMetadata(err)
        });
      }
      this.managed.delete(req.sessionId);
      const failed = await this.registry.update(req.sessionId, {
        state: "failed",
        lastError: message2,
        pid: void 0
      });
      if (!err?.terminalAlreadyEmitted) {
        this.emit({ type: "session.failed", sessionId: req.sessionId, error: message2 });
      }
      throw Object.assign(new Error(message2), {
        session: handleFrom(failed),
        ...copyRuntimeErrorMetadata(err)
      });
    }
  }
  async resumeSession(req) {
    this.assertOpen();
    if (this.startInFlight.has(req.sessionId)) {
      throw new Error(`Session start already in progress: ${req.sessionId}`);
    }
    const existingResume = this.resumeInFlight.get(req.sessionId);
    if (existingResume) return existingResume;
    const operation = this.resumeSessionExclusive(req);
    this.resumeInFlight.set(req.sessionId, operation);
    try {
      return await operation;
    } finally {
      if (this.resumeInFlight.get(req.sessionId) === operation) {
        this.resumeInFlight.delete(req.sessionId);
      }
    }
  }
  async resumeSessionExclusive(req) {
    const record = await this.registry.read(req.sessionId);
    if (!record) throw new Error(`Session not found: ${req.sessionId}`);
    const route = this.connectionForResume(record);
    if (!record.adapterId) throw new Error(`Session ${req.sessionId} has no adapter binding`);
    const adapter = record.adapterId ? this.adapters.get(record.adapterId) : void 0;
    if (!adapter) throw new Error(`Unknown adapter: ${record.adapterId}`);
    const tokenRaw = record.resumeToken;
    if (!tokenRaw) {
      throw new Error(`Session ${req.sessionId} has no resume token`);
    }
    if (!adapter.capabilities().canResume) {
      throw new Error(`Adapter ${adapter.id} cannot resume`);
    }
    const recordedCwd = record.runtimeWorkspace?.cwd;
    const requestedCwd = req.runtimeWorkspace?.cwd ?? req.cwd;
    if (recordedCwd && requestedCwd && !sameRuntimeCwd(recordedCwd, requestedCwd)) {
      throw new Error(
        `resumeSession cwd mismatch: recorded=${recordedCwd} requested=${requestedCwd}`
      );
    }
    const cwd = recordedCwd ?? requestedCwd;
    if (!cwd) throw new Error("resumeSession requires a cwd");
    if (typeof adapter.resumeManagedSession !== "function") {
      throw new Error(
        `Adapter ${adapter.id} advertises canResume but does not implement resumeManagedSession`
      );
    }
    const resumeManagedSession = adapter.resumeManagedSession.bind(adapter);
    let resumedManaged;
    if (SessionRegistry.isNonTerminal(record.state)) {
      const managed = this.managed.get(req.sessionId);
      if (managed?.isAlive()) {
        throw new Error(`Session already active: ${req.sessionId}`);
      }
      this.managed.delete(req.sessionId);
    }
    const now = (/* @__PURE__ */ new Date()).toISOString();
    await this.registry.update(req.sessionId, {
      state: "starting",
      pid: void 0,
      lastError: void 0,
      exitCode: void 0,
      stopReason: void 0,
      runtimeWorkspace: { cwd },
      currentTaskId: req.currentTaskId ?? record.currentTaskId,
      updatedAt: now
    });
    this.emit({ type: "session.starting", sessionId: req.sessionId });
    let resolvedEnv = {};
    let diagnosticSecrets = [];
    try {
      resolvedEnv = await this.resolveLaunchSecretEnv(route);
      const coreEnv = {
        TENT_SERVICE_DATA_DIR: this.dataDir,
        TENT_SESSION_ID: req.sessionId,
        TENT_SESSION_TOKEN: deriveSessionToken(this.sessionTokenKey, req.sessionId)
      };
      const planEnv = {
        ...stripRouteRequestEnv(req.env),
        ...resolvedEnv,
        ...coreEnv
      };
      const acpLaunch = await this.buildAcpLaunchExtras(route, planEnv);
      diagnosticSecrets = Array.from(
        /* @__PURE__ */ new Set([
          ...Object.values(resolvedEnv).filter(
            (v) => typeof v === "string" && v.length > 0
          ),
          ...acpLaunch.diagnosticSecrets
        ])
      );
      const plan = {
        sessionId: req.sessionId,
        connectionId: route.connectionId,
        cwd,
        env: planEnv,
        coreEnv,
        diagnosticSecrets,
        bootstrapPrompt: req.bootstrapPrompt,
        bootstrapImageRefs: req.bootstrapImageRefs,
        command: route.command,
        args: route.args,
        extras: {
          fake: route.fake,
          acp: this.acpOptionsForRoute(route),
          // Resume uses connectionSnapshot (not live catalog edits).
          ...acpLaunch.extras,
          ...req.bootstrapImageRefs && req.bootstrapImageRefs.length > 0 && typeof req.bootstrapImageSystemRoot === "string" && req.bootstrapImageSystemRoot.trim() ? { bootstrapImageSystemRoot: req.bootstrapImageSystemRoot.trim() } : {}
        }
      };
      const resumeToken = adapter.parseResumeToken ? adapter.parseResumeToken(tokenRaw) : { raw: tokenRaw, providerSessionId: tokenRaw };
      let startupCommitted = false;
      let startupLivePid;
      let terminalProjection;
      let terminalDuringManagedStart;
      let managed;
      try {
        managed = await resumeManagedSession(
          plan,
          resumeToken,
          (rawEvent) => {
            const ev = boundRuntimeDiagnosticEvent(rawEvent);
            const managedAtEvent = this.managed.get(req.sessionId) ?? resumedManaged;
            const terminalWhileAlive = (ev.type === "session.failed" || ev.type === "session.exited") && managedAtEvent?.isAlive() === true;
            if (ev.type === "session.failed") {
              terminalDuringManagedStart = { state: "failed", error: ev.error };
              terminalProjection = this.trackManagedTerminal(
                req.sessionId,
                "failed",
                ev.error
              );
            } else if (ev.type === "session.exited") {
              terminalDuringManagedStart = {
                state: "stopped",
                exitCode: ev.exitCode
              };
              terminalProjection = this.trackManagedTerminal(
                req.sessionId,
                "stopped",
                void 0,
                ev.exitCode
              );
            } else if (ev.type === "session.waiting_user") {
              void this.registry.update(req.sessionId, { state: "waiting-user" }).catch(() => void 0);
            } else if (ev.type === "session.live") {
              if (!startupCommitted) {
                startupLivePid = ev.pid;
                return;
              }
              void this.registry.update(req.sessionId, {
                state: "live",
                ...ev.pid != null ? { pid: ev.pid } : {}
              }).catch(() => void 0);
            } else if (ev.type === "session.config_options") {
              void this.registry.update(req.sessionId, {
                acpSession: cloneAcpSessionConfigSnapshot(ev.sessionConfig)
              }).catch(() => void 0);
            } else if (ev.type === "session.acp_observation") {
              void this.registry.update(req.sessionId, {
                acpObservation: { ...ev.observation }
              }).catch(() => void 0);
            }
            if (!terminalWhileAlive) this.emit(ev);
          }
        );
      } catch (error) {
        if (error instanceof ManagedSessionStartupError) {
          resumedManaged = error.managedSession;
          this.managed.set(req.sessionId, resumedManaged);
        }
        throw error;
      }
      resumedManaged = managed;
      if (terminalDuringManagedStart) {
        const terminal = terminalDuringManagedStart;
        await (terminalProjection ?? this.trackManagedTerminal(
          req.sessionId,
          terminal.state,
          terminal.state === "failed" ? terminal.error : void 0,
          terminal.state === "stopped" ? terminal.exitCode : void 0
        ));
        throw Object.assign(
          new Error(
            terminal.state === "failed" ? terminal.error : `Managed session exited during resume (code=${terminal.exitCode})`
          ),
          { terminalAlreadyEmitted: true }
        );
      }
      const pid = managed.pid;
      const expectedProviderSessionId = resumeToken.providerSessionId?.trim();
      const actualProviderSessionId = managed.providerSessionId?.trim();
      if (!expectedProviderSessionId || !actualProviderSessionId) {
        throw new Error(
          `Provider resume did not prove the original conversation identity for Session ${req.sessionId}`
        );
      }
      if (actualProviderSessionId !== expectedProviderSessionId) {
        throw new Error(
          `Provider resumed a different conversation for Session ${req.sessionId}`
        );
      }
      this.managed.set(req.sessionId, managed);
      const live = await this.registry.update(req.sessionId, {
        state: "live",
        pid,
        resumeToken: tokenRaw,
        ...managed.acpSession ? {
          acpSession: cloneAcpSessionConfigSnapshot(managed.acpSession)
        } : {},
        // Native resume reuses provider context — honest continuity claim.
        providerContextRestored: true,
        lastError: void 0,
        exitCode: void 0,
        stopReason: void 0,
        runtimeWorkspace: { cwd }
      });
      startupCommitted = true;
      this.emit({
        type: "session.live",
        sessionId: req.sessionId,
        pid: startupLivePid ?? pid
      });
      return handleFrom(live);
    } catch (err) {
      let retainedManaged = false;
      let stopError;
      if (resumedManaged) {
        try {
          await resumedManaged.stop("interrupt");
        } catch (error) {
          stopError = error;
        }
        retainedManaged = resumedManaged.isAlive();
        if (retainedManaged) {
          this.managed.set(req.sessionId, resumedManaged);
        } else {
          this.managed.delete(req.sessionId);
          await this.waitForManagedTerminal(req.sessionId, true);
        }
      }
      const rawMessage = retainedManaged ? `${err instanceof Error ? err.message : String(err)}; ${stopError instanceof Error ? stopError.message : "managed child exit was not confirmed"}` : err instanceof Error ? err.message : String(err);
      const tokenRedacted = redactRuntimeValue(rawMessage, tokenRaw);
      const message2 = redactDiagnosticText(tokenRedacted, {
        env: {
          ...req.env ?? {}
        },
        secrets: diagnosticSecrets
      });
      if (retainedManaged) {
        const retained = await this.registry.update(req.sessionId, {
          state: "starting",
          pid: resumedManaged?.pid,
          lastError: message2
        });
        throw Object.assign(new Error(message2), {
          session: handleFrom(retained),
          ...copyRuntimeErrorMetadata(err)
        });
      }
      this.managed.delete(req.sessionId);
      const failed = await this.registry.update(req.sessionId, {
        state: "failed",
        lastError: message2,
        pid: void 0
      });
      if (!err?.terminalAlreadyEmitted) {
        this.emit({ type: "session.failed", sessionId: req.sessionId, error: message2 });
      }
      throw Object.assign(new Error(message2), {
        session: handleFrom(failed),
        ...copyRuntimeErrorMetadata(err)
      });
    }
  }
  /** Resume only from the immutable non-secret Agent Connection snapshot. */
  connectionForResume(record) {
    const snapshot = record.connectionSnapshot;
    if (!record.connectionId || !record.adapterId || !snapshot) {
      throw new Error(`Session ${record.id} has no Agent Connection snapshot`);
    }
    if (snapshot.connectionId !== record.connectionId) {
      throw new Error(
        `Session Connection snapshot id mismatch: row=${record.connectionId} snapshot=${snapshot.connectionId}`
      );
    }
    if (snapshot.adapterId !== record.adapterId) {
      throw new Error(
        `Session Connection snapshot adapter mismatch: row=${record.adapterId} snapshot=${snapshot.adapterId}`
      );
    }
    const route = connectionConfigFromSnapshot(snapshot);
    const currentEndpointDigest = this.effectiveEndpointDigest(route);
    if ((snapshot.effectiveEndpointDigest || "") !== (currentEndpointDigest || "")) {
      throw new Error(`Session Connection endpoint changed; provider continuity is no longer valid`);
    }
    const launchDigest = calculateAgentConnectionLaunchDigest(route, currentEndpointDigest);
    if (launchDigest !== snapshot.launchDigest) {
      throw new Error(`Session Connection snapshot launch digest mismatch`);
    }
    return route;
  }
  /**
   * U2A follow-up on a live managed session: send a fixed-format user answer
   * as the next session/prompt. Not multi-turn chat. Throws when the session
   * is not live with a structured prompt transport.
   */
  async sendFollowUpPrompt(sessionId, prompt) {
    this.assertOpen();
    const text3 = prompt.trim();
    if (!text3) throw new Error("sendFollowUpPrompt requires non-empty prompt");
    this.followUpAttemptsForTests.push({ sessionId });
    const managed = this.managed.get(sessionId);
    if (!managed || !managed.isAlive()) {
      throw new Error(`Session not alive for follow-up: ${sessionId}`);
    }
    if (typeof managed.sendFollowUpPrompt !== "function") {
      throw new Error(
        `Session ${sessionId} adapter does not support live follow-up prompts`
      );
    }
    await this.registry.update(sessionId, { state: "live" });
    await managed.sendFollowUpPrompt(text3);
  }
  /** Test helper: follow-up inject attempts (sessionId only; no prompt body). */
  getFollowUpAttemptsForTests() {
    return this.followUpAttemptsForTests.slice();
  }
  /** Test helper: clear follow-up attempt log. */
  clearFollowUpAttemptsForTests() {
    this.followUpAttemptsForTests.length = 0;
  }
  async stopSession(sessionId, reason) {
    this.assertOpen();
    await this.stopSessionInternal(sessionId, reason);
  }
  async stopSessionInternal(sessionId, reason) {
    const record = await this.registry.read(sessionId);
    if (!record) throw new Error(`Session not found: ${sessionId}`);
    if (record.state === "external") {
      await this.registry.update(sessionId, {
        state: "stopped",
        stopReason: reason,
        pid: void 0
      });
      this.emit({
        type: "session.exited",
        sessionId,
        exitCode: 0
      });
      return;
    }
    if (SessionRegistry.isNonTerminal(record.state) || record.state === "starting") {
      await this.registry.update(sessionId, { stopReason: reason });
    }
    const managed = this.managed.get(sessionId);
    try {
      if (managed) {
        await managed.stop(reason);
        if (managed.isAlive()) {
          throw new Error(`Stop failed for session ${sessionId}: child exit was not confirmed`);
        }
        await this.waitForManagedTerminal(sessionId);
        this.managed.delete(sessionId);
      } else if (this.supervisor.isAlive(sessionId)) {
        await this.supervisor.stop(sessionId, { signal: "SIGTERM" });
      }
      await this.waitForChildExit(sessionId);
    } catch (error) {
      const message2 = truncateUtf8Text(
        error instanceof Error ? error.message : String(error),
        ACP_DIAGNOSTIC_EVENT_BYTES
      );
      const current2 = await this.registry.read(sessionId);
      if (current2) {
        await this.registry.update(sessionId, {
          stopReason: reason,
          lastError: message2
        });
      }
      throw new Error(message2);
    }
    const current = await this.registry.read(sessionId);
    if (!current) return;
    if (SessionRegistry.isNonTerminal(current.state)) {
      await this.registry.update(sessionId, {
        state: "stopped",
        stopReason: reason,
        pid: void 0
      });
      this.emit({
        type: "session.exited",
        sessionId,
        exitCode: current.exitCode ?? 0
      });
    } else {
      await this.registry.update(sessionId, {
        stopReason: reason,
        pid: void 0,
        // A provider may report "failed/interrupted" while honoring Tent's
        // explicit stop. The provider message remains diagnostic in lastError,
        // but the lifecycle outcome is an intentional stop.
        state: "stopped"
      });
    }
  }
  async probe(sessionId) {
    const record = await this.registry.read(sessionId);
    if (!record) {
      return {
        sessionId,
        state: "failed",
        isAlive: false,
        canResume: false,
        lastError: "session not found"
      };
    }
    if (record.state === "external") {
      return {
        sessionId,
        state: "external",
        isAlive: true,
        canResume: false,
        lastError: record.lastError,
        exitCode: record.exitCode
      };
    }
    const managed = this.managed.get(sessionId);
    const isAlive = managed ? managed.isAlive() : this.supervisor.isAlive(sessionId);
    const isTurnActive = typeof managed?.isTurnActive === "function" ? managed.isTurnActive() : false;
    const adapter = record.adapterId ? this.adapters.get(record.adapterId) : void 0;
    const canResume = Boolean(
      record.connectionSnapshot && record.resumeToken && adapter?.capabilities().canResume
    );
    if (SessionRegistry.isNonTerminal(record.state) && !isAlive) {
      if (managed) this.managed.delete(sessionId);
      const nextState = canResume ? "stopped" : "failed";
      const updated = await this.registry.update(sessionId, {
        state: nextState,
        pid: void 0,
        lastError: record.lastError ?? (canResume ? "process not alive; resume token retained" : "process not alive and not resume-capable")
      });
      return {
        sessionId,
        state: updated.state,
        isAlive: false,
        canResume,
        isTurnActive: false,
        lastError: updated.lastError,
        exitCode: updated.exitCode
      };
    }
    return {
      sessionId,
      state: record.state,
      isAlive,
      canResume,
      isTurnActive,
      pid: isAlive ? record.pid : void 0,
      lastError: record.lastError,
      exitCode: record.exitCode
    };
  }
  /**
   * On service start: probe all non-terminal sessions and reconcile.
   * Dead PID + not resume-capable → failed/stopped; resume-capable → keep metadata.
   * Note: managed ACP clients do not survive process restart — probe marks them dead.
   */
  async reconcileOnBoot() {
    const all2 = await this.registry.list();
    const results = [];
    for (const rec of all2) {
      if (rec.state === "reserved") {
        await this.registry.update(rec.id, {
          state: "failed",
          pid: void 0,
          lastError: rec.lastError ?? "reserved Session did not reach provider start before Service restart"
        });
        results.push(await this.probe(rec.id));
        continue;
      }
      if (!SessionRegistry.isNonTerminal(rec.state)) continue;
      results.push(await this.probe(rec.id));
    }
    return results;
  }
  /** Service shutdown: stop push children this runtime started (window close does not call this). */
  async shutdown() {
    if (this.shutdownPromise) return this.shutdownPromise;
    if (this.closed) return;
    this.closing = true;
    this.shutdownPromise = this.shutdownInternal();
    return this.shutdownPromise;
  }
  async shutdownInternal() {
    const stopErrors = [];
    try {
      await Promise.allSettled([
        ...this.startInFlight.values(),
        ...this.resumeInFlight.values()
      ]);
      const managedIds = [...this.managed.keys()];
      const live = /* @__PURE__ */ new Set([...this.supervisor.listLive(), ...managedIds]);
      for (const id of live) {
        try {
          await this.stopSessionInternal(id, "shutdown");
        } catch (error) {
          stopErrors.push(error instanceof Error ? error.message : String(error));
        }
      }
      if (stopErrors.length > 0) {
        throw new Error(
          truncateUtf8Text(
            `Runtime shutdown could not confirm child exit: ${stopErrors.join("; ")}`,
            ACP_DIAGNOSTIC_EVENT_BYTES
          )
        );
      }
    } finally {
      this.closed = true;
    }
  }
  /**
   * Managed ACP terminal path (no ProcessSupervisor exit). Idempotent:
   * second failure/exit does not illegal-transition the session row.
   */
  trackManagedTerminal(sessionId, terminalState, lastError, exitCode) {
    const projection = this.onManagedTerminal(
      sessionId,
      terminalState,
      lastError,
      exitCode
    ).catch(
      () => this.onManagedTerminal(sessionId, terminalState, lastError, exitCode)
    );
    let pending = this.managedTerminalInFlight.get(sessionId);
    if (!pending) {
      pending = /* @__PURE__ */ new Set();
      this.managedTerminalInFlight.set(sessionId, pending);
    }
    pending.add(projection);
    void projection.finally(() => {
      pending.delete(projection);
      if (pending.size === 0 && this.managedTerminalInFlight.get(sessionId) === pending) {
        this.managedTerminalInFlight.delete(sessionId);
      }
    }).catch(() => void 0);
    return projection;
  }
  async waitForManagedTerminal(sessionId, suppressError = false) {
    let firstError;
    while (true) {
      const pending = this.managedTerminalInFlight.get(sessionId);
      if (!pending?.size) {
        if (!suppressError && firstError !== void 0) throw firstError;
        return;
      }
      const snapshot = [...pending];
      const results = await Promise.allSettled(snapshot);
      if (!suppressError && firstError === void 0) {
        const rejected = results.find(
          (result) => result.status === "rejected"
        );
        if (rejected) firstError = rejected.reason;
      }
    }
  }
  async onManagedTerminal(sessionId, terminalState, lastError, exitCode) {
    const managed = this.managed.get(sessionId);
    if (managed?.isAlive()) {
      const message2 = truncateUtf8Text(
        lastError ?? "managed terminal event arrived before child exit confirmation",
        ACP_DIAGNOSTIC_EVENT_BYTES
      );
      const current = await this.registry.read(sessionId);
      if (current) {
        await this.registry.update(sessionId, { lastError: message2 });
      }
      return;
    }
    this.managed.delete(sessionId);
    const record = await this.registry.read(sessionId);
    if (!record) return;
    if (!SessionRegistry.isNonTerminal(record.state) && record.state !== "starting") {
      await this.registry.update(sessionId, {
        pid: void 0,
        ...lastError ? { lastError } : {},
        ...exitCode !== void 0 ? { exitCode } : {}
      });
      return;
    }
    await this.registry.update(sessionId, {
      state: terminalState,
      pid: void 0,
      lastError: lastError ?? record.lastError,
      ...exitCode !== void 0 ? { exitCode } : {}
    });
  }
  async onChildExit(sessionId, exitCode, signal) {
    const record = await this.registry.read(sessionId);
    if (!record) return;
    const adapter = record.adapterId ? this.adapters.get(record.adapterId) : void 0;
    let event;
    if (adapter) {
      event = adapter.mapExit(exitCode, signal);
      event = { ...event, sessionId };
    } else if (exitCode === 0 || signal === "SIGTERM" || signal === "SIGINT") {
      event = { type: "session.exited", sessionId, exitCode };
    } else {
      event = {
        type: "session.failed",
        sessionId,
        error: signal ? `signal:${signal}` : `exit:${exitCode}`
      };
    }
    if (event.type === "session.failed") {
      const tail = this.supervisor.getStdoutTail(sessionId).trim();
      if (tail) {
        const snippet = tail.slice(-500);
        event = {
          ...event,
          error: `${event.error} (stderr: ${snippet})`
        };
      }
    }
    const terminalState = event.type === "session.failed" ? "failed" : "stopped";
    if (SessionRegistry.isNonTerminal(record.state) || record.state === "starting") {
      await this.registry.update(sessionId, {
        state: terminalState,
        pid: void 0,
        exitCode,
        lastError: event.type === "session.failed" ? event.error : record.lastError
      });
    } else {
      await this.registry.update(sessionId, {
        pid: void 0,
        exitCode
      });
    }
    this.emit(event);
  }
  async waitForChildExit(sessionId, suppressError = false) {
    const projection = this.childExitInFlight.get(sessionId);
    if (!projection) return;
    if (suppressError) {
      await projection.catch(() => void 0);
      return;
    }
    await projection;
  }
  emit(ev) {
    ev = boundRuntimeDiagnosticEvent(ev);
    for (const sink of this.globalSinks) {
      try {
        sink(ev);
      } catch {
      }
    }
    const set = this.sinks.get(ev.sessionId);
    if (!set) return;
    for (const sink of set) {
      try {
        sink(ev);
      } catch {
      }
    }
  }
  /**
   * Resolve skill metadata + MCP wire from the Connection snapshot.
   * Secret values only live on the plan (in-process) for session/new|load — never SessionRecord.
   * Enabled skill path refs fail loud when missing; launch-secret resolver errors are not swallowed.
   *
   * Built-in tent-role / tent-task contracts are injected only into the managed bootstrap
   * prompt prefix (cross-provider). ACP `_meta.tent.skills` carries optional Connection skills
   * extras only — never re-advertise built-ins as activatable skill refs.
   */
  async buildAcpLaunchExtras(route, planEnv) {
    const composedSkills = composeManagedSkillRefs({
      packageRoot: this.packageRoot ?? "",
      connectionSkills: route.skills
    });
    const hasSkills = Array.isArray(composedSkills) && composedSkills.length > 0;
    const hasMcp = Array.isArray(route.mcpServers) && route.mcpServers.length > 0;
    if (!hasSkills && !hasMcp) return { extras: {}, diagnosticSecrets: [] };
    const credCache = /* @__PURE__ */ new Map();
    if (hasMcp && this.resolveLaunchSecretRef) {
      for (const s of route.mcpServers ?? []) {
        if (s.enabled === false) continue;
        const refs = /* @__PURE__ */ new Set();
        if (s.envSecretRefs) {
          for (const id of Object.values(s.envSecretRefs)) refs.add(id);
        }
        if (s.headerSecretRefs) {
          for (const id of Object.values(s.headerSecretRefs)) refs.add(id);
        }
        for (const id of refs) {
          if (credCache.has(id)) continue;
          let value;
          try {
            value = await this.resolveLaunchSecretRef(id);
          } catch {
            throw new Error(
              `MCP server ${s.name}: launch-secret resolve failed for Agent Connection ${route.connectionId} launchSecretRef=${id}`
            );
          }
          if (typeof value === "string" && value) {
            credCache.set(id, value);
          }
        }
      }
    }
    const acpSkills = hasSkills ? resolveAcpSkillMeta(composedSkills, { requirePathExists: true }) : void 0;
    const acpMcpServers = hasMcp ? resolveAcpMcpServersWire(route.mcpServers, {
      planEnv,
      resolveLaunchSecret: (id) => credCache.get(id)
    }) : void 0;
    return {
      extras: {
        ...acpSkills !== void 0 ? { acpSkills } : {},
        ...acpMcpServers !== void 0 ? { acpMcpServers } : {}
      },
      // Ephemeral redaction inputs only. These values are already present in the
      // ACP MCP launch wire and must never reach SessionRegistry or diagnostics.
      diagnosticSecrets: Array.from(new Set(credCache.values()))
    };
  }
  /**
   * Resolve launch-secret and endpoint env values for one launch only.
   * Never persists secrets onto SessionRecord.
   */
  async resolveLaunchSecretEnv(route) {
    const out = {};
    const ref = route.launchSecretRef?.trim() || "";
    const envKey = route.envKey?.trim() || "";
    if (ref && !envKey) {
      throw new Error(
        `Agent Connection ${route.connectionId} has launchSecretRef but no envKey`
      );
    }
    if (ref && !this.resolveConnectionEnv) {
      throw new Error(
        `Agent Connection ${route.connectionId} references launch secret ${ref} but AgentRuntime has no resolveConnectionEnv hook`
      );
    }
    if (ref) {
      const resolved = { ...await this.resolveConnectionEnv(route) };
      const secret = resolved[envKey];
      if (typeof secret !== "string" || !secret) {
        throw new Error(
          `Launch secret not found or empty for Agent Connection ${route.connectionId} (launchSecretRef=${ref})`
        );
      }
      out[envKey] = secret;
    }
    return out;
  }
  effectiveEndpointDigest(route) {
    const raw = route.endpoint;
    const normalized = raw?.trim();
    if (!normalized) return void 0;
    return `sha256:${createHash6("sha256").update(normalized).digest("hex")}`;
  }
  acpOptionsForRoute(route) {
    return {
      model: route.model,
      envKey: route.envKey,
      launchSecretRef: route.launchSecretRef,
      endpoint: route.endpoint,
      permissionPolicy: route.permissionPolicy,
      promptTimeoutMs: route.promptTimeoutMs,
      permissionTimeoutMs: route.permissionTimeoutMs
    };
  }
  assertOpen() {
    if (this.closed || this.closing) throw new Error("AgentRuntime is shut down");
  }
};
function sameRuntimeCwd(left, right) {
  const a = path19.resolve(left);
  const b = path19.resolve(right);
  return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
}
function redactRuntimeValue(message2, value) {
  return value ? message2.split(value).join("[provider-session]") : message2;
}
function stripRouteRequestEnv(env) {
  return stripReservedTentChildEnv(env);
}
function createAgentRuntime(options) {
  return new AgentRuntime(options);
}

// src/service/service.ts
import * as os6 from "node:os";
import * as path21 from "node:path";
import { fileURLToPath } from "node:url";

// src/service/service-lease.ts
import { randomUUID as randomUUID2 } from "node:crypto";
import * as fs20 from "node:fs/promises";
import * as path20 from "node:path";
var ServiceDataDirBusyError = class extends Error {
  constructor(dataDir, owner) {
    super(
      `Local Tent Service data directory is already owned by pid ${owner.pid} (instance ${owner.instanceId}): ${dataDir}`
    );
    this.name = "ServiceDataDirBusyError";
    this.owner = owner;
  }
};
function serviceLeasePath(dataDir) {
  return path20.join(dataDir, "service.lock");
}
async function acquireServiceDataDirLease(dataDir, options = {}) {
  const pid = options.pid ?? process.pid;
  const instanceId = options.makeInstanceId?.() ?? randomUUID2();
  const startedAt = (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
  const isProcessAlive = options.isProcessAlive ?? processIsAlive2;
  const record = { instanceId, pid, startedAt };
  const lockPath = serviceLeasePath(dataDir);
  await fs20.mkdir(dataDir, { recursive: true });
  const candidate = `${lockPath}.candidate-${instanceId}`;
  await fs20.writeFile(candidate, JSON.stringify(record, null, 2) + "\n", {
    encoding: "utf8",
    flag: "wx"
  });
  try {
    for (; ; ) {
      try {
        await fs20.link(candidate, lockPath);
        break;
      } catch (error) {
        if (!hasCode2(error, "EEXIST")) throw error;
        const owner = await readLeaseRecord(lockPath);
        if (owner && isProcessAlive(owner.pid)) {
          throw new ServiceDataDirBusyError(dataDir, owner);
        }
        const stalePath = `${lockPath}.stale-${randomUUID2()}`;
        try {
          await fs20.rename(lockPath, stalePath);
          try {
            await fs20.rm(stalePath, { force: true });
          } catch {
          }
        } catch (error2) {
          if (hasCode2(error2, "ENOENT")) continue;
          throw error2;
        }
      }
    }
  } finally {
    try {
      await fs20.rm(candidate, { force: true });
    } catch {
    }
  }
  let released = false;
  return {
    ...record,
    path: lockPath,
    release: async () => {
      if (released) return;
      const owner = await readLeaseRecord(lockPath);
      if (owner?.instanceId === instanceId) {
        await fs20.rm(lockPath, { force: true });
      }
      released = true;
    }
  };
}
async function readLeaseRecord(file) {
  try {
    const value = JSON.parse(await fs20.readFile(file, "utf8"));
    if (typeof value.instanceId !== "string" || !value.instanceId || typeof value.pid !== "number" || !Number.isInteger(value.pid) || typeof value.startedAt !== "string") {
      return null;
    }
    return value;
  } catch (error) {
    if (hasCode2(error, "ENOENT")) return null;
    if (error instanceof SyntaxError) return null;
    throw error;
  }
}
function processIsAlive2(pid) {
  if (!Number.isInteger(pid) || pid <= 0) return false;
  try {
    process.kill(pid, 0);
    return true;
  } catch (error) {
    return !hasCode2(error, "ESRCH");
  }
}
function hasCode2(error, code) {
  return !!error && typeof error === "object" && "code" in error && error.code === code;
}

// src/markdown/attachment-refs.ts
import * as nodePath7 from "node:path";
function resolveAttachmentPath(raw, sourcePath) {
  let target = raw.trim();
  if (target.startsWith("<") && target.endsWith(">")) target = target.slice(1, -1).trim();
  if (!target || target.startsWith("#") || target.startsWith("//")) return void 0;
  if (/^[a-z][a-z0-9+.-]*:/i.test(target)) return void 0;
  try {
    target = decodeURIComponent(target);
  } catch {
    return void 0;
  }
  target = (target.split("#")[0]?.split("?")[0] ?? target).replace(/\\/g, "/").trim();
  if (!target) return void 0;
  const systemPrefix = `${TENT_SYSTEM_DIR}/${ATTACHMENTS_DIR}/`;
  if (target.startsWith(systemPrefix)) target = target.slice(TENT_SYSTEM_DIR.length + 1);
  if (target.startsWith(`/${ATTACHMENTS_DIR}/`)) target = target.slice(1);
  let normalized;
  if (target === ATTACHMENTS_DIR || target.startsWith(`${ATTACHMENTS_DIR}/`)) {
    normalized = nodePath7.posix.normalize(target);
  } else if ((target.startsWith("./") || target.startsWith("../")) && sourcePath) {
    normalized = nodePath7.posix.normalize(
      nodePath7.posix.join(nodePath7.posix.dirname(sourcePath.replace(/\\/g, "/")), target)
    );
  } else {
    return void 0;
  }
  if (!normalized.startsWith(`${ATTACHMENTS_DIR}/`) || normalized.includes("\0")) return void 0;
  if (normalized.split("/").some((segment) => segment === ".." || segment === "")) return void 0;
  return normalized;
}
function extractAttachmentReferences(markdown, sourcePath) {
  const tree = fromMarkdown(markdown);
  const definitions = collectDefinitions2(tree);
  const out = [];
  const seen = /* @__PURE__ */ new Set();
  walk2(tree, (node2) => {
    if (node2.type === "image" || node2.type === "link") {
      push2(out, seen, node2.url, node2.type === "image" ? "image" : "link", sourcePath);
      return "skip";
    }
    if (node2.type === "imageReference" || node2.type === "linkReference") {
      const definition2 = definitions.get(normalizeIdentifier3(node2.identifier));
      if (definition2) {
        push2(
          out,
          seen,
          definition2.url,
          node2.type === "imageReference" ? "image" : "link",
          sourcePath
        );
      }
      return "skip";
    }
    if (node2.type === "text") {
      for (const match of node2.value.matchAll(/!\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]/g)) {
        push2(out, seen, match[1] ?? "", "wiki-embed", sourcePath);
      }
    }
  });
  return out;
}
function collectDefinitions2(tree) {
  const definitions = /* @__PURE__ */ new Map();
  walk2(tree, (node2) => {
    if (node2.type === "definition") {
      definitions.set(normalizeIdentifier3(node2.identifier), { url: node2.url });
    }
  });
  return definitions;
}
function normalizeIdentifier3(value) {
  return value.trim().replace(/\s+/g, " ").toLowerCase();
}
function walk2(node2, visit) {
  if (visit(node2) === "skip") return;
  if ("children" in node2 && Array.isArray(node2.children)) {
    for (const child of node2.children) walk2(child, visit);
  }
}
function push2(out, seen, raw, kind, sourcePath) {
  const path23 = resolveAttachmentPath(raw, sourcePath);
  if (!path23) return;
  const key2 = `${kind}:${path23}`;
  if (seen.has(key2)) return;
  seen.add(key2);
  out.push({ path: path23, kind, raw });
}

// src/markdown/attachment-gc.ts
var ATTACHMENT_GC_GRACE_DAYS = 30;
var ATTACHMENT_GC_STATE_PATH = `${ATTACHMENTS_DIR}/.gc-state.json`;
var DAY_MS = 24 * 60 * 60 * 1e3;
async function sweepAttachmentGc(fs21, options = {}) {
  return withTentMutation(fs21, async () => {
    const result = {
      scanned: 0,
      retainedByOwner: 0,
      retainedByReference: 0,
      candidates: 0,
      deleted: [],
      warnings: []
    };
    if (!await fs21.exists(ATTACHMENTS_DIR)) return result;
    const nowMs = resolveNow(options.now);
    const graceDays = options.graceDays ?? ATTACHMENT_GC_GRACE_DAYS;
    if (!Number.isFinite(graceDays) || graceDays < 0) {
      throw new Error("attachment GC graceDays must be a non-negative number");
    }
    let tent;
    try {
      tent = await loadTent(fs21);
    } catch (error) {
      result.warnings.push(`Node scan failed: ${message(error)}`);
      return result;
    }
    if (tent.duplicateIds.size > 0 || [...tent.byPath.values()].some((node2) => node2.invalid)) {
      result.warnings.push("Node index is ambiguous or invalid; attachment deletion skipped");
      return result;
    }
    let files;
    let references;
    try {
      files = (await listFiles(fs21, ATTACHMENTS_DIR)).filter(
        (path23) => path23 !== ATTACHMENT_GC_STATE_PATH
      );
      references = await collectAttachmentReferences(fs21);
    } catch (error) {
      result.warnings.push(`attachment reference scan failed: ${message(error)}`);
      return result;
    }
    result.scanned = files.length;
    const loadedState = await readState(fs21);
    if (loadedState.warning) result.warnings.push(loadedState.warning);
    const state = loadedState.state;
    const nextCandidates = {};
    const owners = new Set(tent.byId.keys());
    const liveFiles = new Set(files);
    for (const file of files) {
      const owner = file.split("/")[1] ?? "";
      if (owner && owners.has(owner)) {
        result.retainedByOwner += 1;
        continue;
      }
      if (references.has(file)) {
        result.retainedByReference += 1;
        continue;
      }
      const firstSeen = loadedState.valid ? state.candidates[file] : void 0;
      const firstSeenMs = firstSeen ? Date.parse(firstSeen) : Number.NaN;
      if (loadedState.valid && Number.isFinite(firstSeenMs) && nowMs - firstSeenMs >= graceDays * DAY_MS) {
        try {
          await fs21.remove(file);
          result.deleted.push(file);
        } catch (error) {
          result.warnings.push(`failed to delete ${file}: ${message(error)}`);
          nextCandidates[file] = firstSeen;
        }
      } else {
        nextCandidates[file] = Number.isFinite(firstSeenMs) ? firstSeen : new Date(nowMs).toISOString();
      }
    }
    for (const path23 of Object.keys(state.candidates)) {
      if (!liveFiles.has(path23)) delete nextCandidates[path23];
    }
    result.candidates = Object.keys(nextCandidates).length;
    await fs21.writeFile(
      ATTACHMENT_GC_STATE_PATH,
      JSON.stringify({ version: 1, candidates: nextCandidates }, null, 2) + "\n"
    );
    await removeEmptyOwnerDirs(fs21, result);
    return result;
  });
}
async function collectAttachmentReferences(fs21) {
  const refs = /* @__PURE__ */ new Set();
  for (const path23 of await listFiles(fs21, "")) {
    if (!path23.endsWith(".md") || path23.startsWith(`${ATTACHMENTS_DIR}/`)) continue;
    const raw = await fs21.readFile(path23);
    const parsed = parseFrontmatter(raw);
    for (const ref of extractAttachmentReferences(parsed.body, path23)) refs.add(ref.path);
  }
  return refs;
}
async function listFiles(fs21, dir) {
  if (dir && !await fs21.exists(dir)) return [];
  const out = [];
  for (const entry of await fs21.listDir(dir)) {
    const path23 = dir ? `${dir}/${entry.name}` : entry.name;
    if (entry.isDir) out.push(...await listFiles(fs21, path23));
    else out.push(path23);
  }
  return out;
}
async function readState(fs21) {
  const empty = { version: 1, candidates: {} };
  if (!await fs21.exists(ATTACHMENT_GC_STATE_PATH)) return { state: empty, valid: true };
  try {
    const parsed = JSON.parse(await fs21.readFile(ATTACHMENT_GC_STATE_PATH));
    if (parsed.version !== 1 || !parsed.candidates || typeof parsed.candidates !== "object") {
      throw new Error("unsupported state shape");
    }
    const candidates = {};
    for (const [path23, firstSeen] of Object.entries(parsed.candidates)) {
      if (path23.startsWith(`${ATTACHMENTS_DIR}/`) && path23 !== ATTACHMENT_GC_STATE_PATH && typeof firstSeen === "string" && Number.isFinite(Date.parse(firstSeen))) {
        candidates[path23] = firstSeen;
      }
    }
    return { state: { version: 1, candidates }, valid: true };
  } catch (error) {
    return {
      state: empty,
      valid: false,
      warning: `attachment GC state reset: ${message(error)}`
    };
  }
}
async function removeEmptyOwnerDirs(fs21, result) {
  for (const entry of await fs21.listDir(ATTACHMENTS_DIR)) {
    if (!entry.isDir) continue;
    const dir = `${ATTACHMENTS_DIR}/${entry.name}`;
    try {
      if ((await fs21.listDir(dir)).length === 0) await fs21.remove(dir);
    } catch (error) {
      result.warnings.push(`failed to remove empty attachment directory ${dir}: ${message(error)}`);
    }
  }
}
function resolveNow(value) {
  const ms = value instanceof Date ? value.getTime() : value ? Date.parse(value) : Date.now();
  if (!Number.isFinite(ms)) throw new Error("attachment GC now must be a valid date");
  return ms;
}
function message(error) {
  return error instanceof Error ? error.message : String(error);
}

// src/service/service.ts
var SERVICE_VERSION = "0.2.0";
async function startLocalTentService(options = {}) {
  const dataDir = options.dataDir ?? defaultServiceDataDir();
  const serviceLease = await acquireServiceDataDirLease(dataDir);
  const startupCleanups = [];
  try {
    return await startOwnedLocalTentService(
      options,
      dataDir,
      serviceLease,
      (phase, action) => startupCleanups.push({ phase, action })
    );
  } catch (error) {
    startupCleanups.sort((a, b) => a.phase - b.phase);
    for (const cleanup of startupCleanups) {
      try {
        await cleanup.action();
      } catch {
      }
    }
    await serviceLease.release();
    throw error;
  }
}
async function startOwnedLocalTentService(options, dataDir, serviceLease, registerStartupCleanup) {
  const version = options.version ?? SERVICE_VERSION;
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
  const getPid = options.getPid ?? (() => process.pid);
  const token = options.token ?? generateServiceToken();
  const events = new EventBus();
  const mutations = new MutationBus();
  const workspaceHost = new WorkspaceHost({
    events,
    housekeeper: async (mount) => {
      await mutations.run(mount.workspaceId, async () => {
        const now = mount.env.clock.now();
        await purgeOperationalRetention(mount.env.fs, { now });
        await sweepAttachmentGc(mount.env.fs, { now });
      });
    }
  });
  registerStartupCleanup(30, () => workspaceHost.dispose());
  const toolApprovals = new ToolApprovalStore(dataDir);
  await toolApprovals.ensureLoaded();
  const decisionRequests = new DecisionRequestStore(dataDir);
  await decisionRequests.ensureLoaded();
  const taskInputs = new TaskInputStore(dataDir);
  await taskInputs.ensureLoaded();
  const managedTaskResultReportDrafts = new ManagedTaskResultReportDraftStore(dataDir);
  await managedTaskResultReportDrafts.ensureLoaded();
  enableManagedTaskInputBackgroundAccept();
  const launchSecrets = new LaunchSecretStore(dataDir, {
    protector: options.launchSecretProtector
  });
  await launchSecrets.ensureLoaded();
  const connectionsInjected = options.connections !== void 0;
  const connections = connectionsInjected ? options.connections : await ensureDefaultAgentConnections(dataDir);
  const runtimeHolder = { current: null };
  const acpPermissionHooks = {
    onPermissionAsk: async (info) => {
      const runtime2 = runtimeHolder.current;
      if (!runtime2) return "deny";
      const rec = await runtime2.registry.read(info.sessionId);
      const workspaceId = resolveToolApprovalWorkspaceId(rec?.workspace);
      if (!workspaceId) return "deny";
      let taskPath;
      let taskId;
      let role = rec?.roleId;
      try {
        const mount = workspaceHost.get(workspaceId);
        if (mount) {
          const tasks = await loadTaskRecords(mount.env.fs);
          const task = tasks.find(
            (t) => t.executionSessionId === info.sessionId || !!rec?.currentTaskId && (t.id === rec.currentTaskId || t.path === rec.currentTaskId)
          );
          if (task) {
            taskPath = task.path;
            taskId = task.id || task.path;
            role = task.requester?.kind === "role" ? task.requester.id : role;
          }
        }
      } catch {
      }
      const timeoutMs = typeof rec?.connectionSnapshot?.permissionTimeoutMs === "number" && rec.connectionSnapshot.permissionTimeoutMs > 0 ? rec.connectionSnapshot.permissionTimeoutMs : DEFAULT_PERMISSION_TIMEOUT_MS;
      const createdAt = /* @__PURE__ */ new Date();
      const expiresAt = new Date(createdAt.getTime() + timeoutMs);
      const item = await toolApprovals.add({
        id: makeToolApprovalId(),
        workspaceId,
        sessionId: info.sessionId,
        taskId,
        taskPath,
        role,
        toolTitle: info.toolTitle || "tool",
        toolCallId: info.toolCallId,
        options: (info.options ?? []).map((o) => ({
          optionId: o.optionId,
          kind: o.kind,
          name: o.name
        })),
        status: "pending",
        createdAt: createdAt.toISOString(),
        expiresAt: expiresAt.toISOString()
      });
      events.emit(
        "toolApproval.pending",
        workspaceId,
        {
          approvalId: item.id,
          sessionId: item.sessionId,
          taskPath: item.taskPath,
          role: item.role,
          toolTitle: item.toolTitle,
          expiresAt: item.expiresAt
        },
        "service"
      );
      const decision = await toolApprovals.waitForDecision(item.id, timeoutMs);
      return decision === "approved" ? "allow" : "deny";
    }
  };
  const packageRootEarly = options.packageRoot ?? defaultPackageRoot();
  const runtime = createAgentRuntime({
    dataDir,
    sessionTokenKey: token,
    connections,
    packageRoot: packageRootEarly,
    adapters: [
      createFakeAdapter(),
      createGrokAcpAdapter(acpPermissionHooks),
      createCodexAcpAdapter(acpPermissionHooks),
      createClaudeAcpAdapter(acpPermissionHooks),
      createOpenCodeAcpAdapter(acpPermissionHooks),
      createCopilotAcpAdapter(acpPermissionHooks),
      createPiAcpAdapter(acpPermissionHooks)
    ],
    resolveConnectionEnv: async (connection) => {
      const ref = connection.launchSecretRef?.trim() || "";
      if (!ref) return {};
      const envKey = connection.envKey?.trim() || "";
      if (!envKey) {
        throw new Error(
          `Agent Connection ${connection.connectionId} has launchSecretRef but no envKey`
        );
      }
      const secret = await launchSecrets.resolve(ref);
      if (!secret) {
        throw new Error(
          `Launch secret not found or empty for Agent Connection ${connection.connectionId} (launchSecretRef=${ref})`
        );
      }
      return { [envKey]: secret };
    },
    resolveLaunchSecretRef: async (launchSecretRef) => {
      const id = typeof launchSecretRef === "string" ? launchSecretRef.trim() : "";
      if (!id) return void 0;
      const secret = await launchSecrets.resolve(id);
      return typeof secret === "string" && secret ? secret : void 0;
    }
  });
  runtimeHolder.current = runtime;
  registerStartupCleanup(10, async () => {
    try {
      await runtime.shutdown();
    } catch {
    }
  });
  const connectionCatalog = new AgentConnectionCatalog(dataDir, connections, {
    // Normal boot: persist CRUD to this service dataDir.
    // Injected Connections are in-memory only — no connections.json writes.
    persistToDisk: !connectionsInjected,
    publishConnections: (next) => runtime.replaceConnectionCatalog(next)
  });
  await runtime.reconcileOnBoot();
  const packageRoot = packageRootEarly;
  const home = options.home ?? os6.homedir();
  const ctx = {
    host: workspaceHost,
    mutations,
    events,
    version,
    protocolVersion: TENT_SERVICE_PROTOCOL_VERSION,
    instanceId: serviceLease.instanceId,
    startedAt,
    getPid,
    runtime,
    toolApprovals,
    decisionRequests,
    taskInputs,
    managedTaskResultReportDrafts,
    launchSecrets,
    dataDir,
    connectionCatalog,
    packageRoot,
    home,
    integrateCommits: options.integrateCommits
  };
  const runtimeProjections = /* @__PURE__ */ new Set();
  const unsubscribeRuntimeEvents = runtime.subscribeAll((ev) => {
    const projection = mapRuntimeEventToService(ctx, ev);
    runtimeProjections.add(projection);
    void projection.then(
      () => runtimeProjections.delete(projection),
      () => runtimeProjections.delete(projection)
    );
  });
  const drainRuntimeProjections = async () => {
    while (runtimeProjections.size > 0) {
      await Promise.allSettled([...runtimeProjections]);
    }
  };
  registerStartupCleanup(20, async () => {
    await drainRuntimeProjections();
    unsubscribeRuntimeEvents();
  });
  const httpServer = await createServiceHttpServer({
    host: options.host ?? "127.0.0.1",
    port: options.port ?? 0,
    ctx,
    events,
    token
  });
  registerStartupCleanup(5, () => httpServer.close());
  let endpoint = null;
  if (options.writeEndpoint !== false) {
    endpoint = {
      instanceId: serviceLease.instanceId,
      pid: getPid(),
      host: httpServer.host,
      port: httpServer.port,
      startedAt,
      version,
      token
    };
    await writeServiceEndpoint(dataDir, endpoint);
    registerStartupCleanup(
      50,
      () => removeServiceEndpoint(dataDir, endpoint)
    );
  }
  events.emit("service.health", "", {
    action: "started",
    url: httpServer.url,
    pid: getPid()
  });
  let stopPromise = null;
  const stop = () => {
    if (stopPromise) return stopPromise;
    stopPromise = (async () => {
      let firstError;
      const attempt = async (action, bestEffort = false) => {
        try {
          await action();
        } catch (error) {
          if (!bestEffort && firstError === void 0) firstError = error;
        }
      };
      try {
        events.emit("service.health", "", { action: "stopping" });
        await attempt(() => httpServer.close());
        await attempt(() => toolApprovals.shutdown(), true);
        await attempt(() => decisionRequests.shutdown(), true);
        stopManagedTaskInputBackgroundAccept();
        await attempt(() => runtime.shutdown());
        await attempt(
          () => drainManagedTaskInputBackgroundForShutdown(5e3),
          true
        );
        await attempt(() => taskInputs.shutdown(), true);
        await attempt(() => managedTaskResultReportDrafts.shutdown(), true);
        await attempt(() => drainRuntimeProjections());
        unsubscribeRuntimeEvents();
        await attempt(() => workspaceHost.dispose());
      } finally {
        if (options.writeEndpoint !== false) {
          await attempt(
            () => removeServiceEndpoint(dataDir, endpoint)
          );
        }
        await attempt(() => serviceLease.release());
      }
      if (firstError !== void 0) throw firstError;
    })();
    return stopPromise;
  };
  return {
    url: httpServer.url,
    host: httpServer.host,
    port: httpServer.port,
    dataDir,
    token,
    events,
    hostApi: workspaceHost,
    runtime,
    launchSecrets,
    ctx,
    endpoint,
    stop
  };
}
function defaultPackageRoot() {
  const here = path21.dirname(fileURLToPath(import.meta.url));
  if (path21.basename(here) === "service" && path21.basename(path21.dirname(here)) === "src") {
    return path21.resolve(here, "../..");
  }
  return path21.resolve(here);
}

// src/service/cli.ts
async function main() {
  const args = process.argv.slice(2);
  const cmd = args[0] ?? "start";
  if (cmd === "help" || cmd === "--help" || cmd === "-h") {
    process.stdout.write(
      [
        "tent-service \u2014 Local Tent Service",
        "",
        "Usage:",
        "  tent-service start [--port <n>] [--data-dir <path>] [--mount <workspace>]",
        "  tent-service status",
        "",
        "Environment:",
        "  TENT_SERVICE_DATA_DIR  machine-local data area (default: %APPDATA%/Tent)",
        "",
        "Auth:",
        "  Loopback token is written to an immutable endpoint generation under <dataDir> and required on /rpc + /events.",
        "  GET /health remains open for unauthenticated liveness diagnostics only; attach identity uses authenticated service.health RPC.",
        ""
      ].join("\n")
    );
    return;
  }
  if (cmd === "status") {
    const dataDir2 = flagValue(args, "--data-dir") ?? defaultServiceDataDir();
    const ep = await readServiceEndpoint(dataDir2);
    if (!ep) {
      process.stdout.write(`No service endpoint in ${dataDir2}
`);
      process.exitCode = 1;
      return;
    }
    process.stdout.write(JSON.stringify(ep, null, 2) + "\n");
    return;
  }
  if (cmd !== "start") {
    process.stderr.write(`Unknown command: ${cmd}
`);
    process.exitCode = 2;
    return;
  }
  const portRaw = flagValue(args, "--port");
  const dataDir = flagValue(args, "--data-dir");
  const mountPath = flagValue(args, "--mount");
  const service = await startLocalTentService({
    port: portRaw ? Number(portRaw) : 0,
    dataDir: dataDir ? path22.resolve(dataDir) : void 0
  });
  if (mountPath) {
    const info = await service.hostApi.mount(path22.resolve(mountPath));
    process.stdout.write(`Mounted ${info.workspaceRoot} as ${info.workspaceId}
`);
  }
  process.stdout.write(
    `Local Tent Service listening on ${service.url}
dataDir: ${service.dataDir}
pid: ${process.pid}
token: (written to an immutable endpoint generation under dataDir; required on /rpc and /events)
Attach: POST ${service.url}/rpc  |  events: GET ${service.url}/events  |  health: GET ${service.url}/health
`
  );
  const shutdown = async (signal) => {
    process.stdout.write(`
Stopping (${signal})...
`);
    await service.stop();
    process.exit(0);
  };
  process.on("SIGINT", () => void shutdown("SIGINT"));
  process.on("SIGTERM", () => void shutdown("SIGTERM"));
}
function flagValue(args, name) {
  const i = args.indexOf(name);
  if (i === -1) return void 0;
  return args[i + 1];
}
main().catch((error) => {
  process.stderr.write((error instanceof Error ? error.stack || error.message : String(error)) + "\n");
  process.exit(1);
});
