@zooid/core 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/env-interpolation.ts","../src/acp-registry.ts","../src/approval-correlator.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { isAbsolute, join, resolve as pathResolve } from 'node:path'\nimport { parse } from 'yaml'\nimport type { AcpAgentSpec } from './acp-types.js'\nimport { isPreset } from '@zooid/acp-client'\nimport { interpolateEnv, interpolateString } from './env-interpolation.js'\nimport type {\n AgentConfig,\n CliFlags,\n ContainerConfig,\n HttpBinding,\n HttpTransportConfig,\n MatrixBinding,\n MatrixTransportConfig,\n MountConfig,\n RoomBinding,\n TransportConfig,\n ZooidConfig,\n ZooidContainerConfig,\n} from './types.js'\n\nconst SLUG_RE = /^[a-z0-9-]+$/\n\nexport interface LoadZooidConfigOptions {\n /**\n * Directory containing zooid.yaml. Required when any agent uses a\n * relative `container.mounts[].host` path; resolution happens at parse\n * time so the resulting `MountConfig` always carries an absolute host\n * path.\n */\n configDir?: string\n}\n\nconst AGENT_NAME_RE = /^[a-z][a-z0-9-]{0,31}$/\nconst MATRIX_USER_ID_RE = /^@[A-Za-z0-9._\\-=/+]+:[A-Za-z0-9.\\-]+$/\nconst MATRIX_USER_LOCALPART_RE = /^@[a-z0-9._=/+\\-]+$/\nconst MATRIX_ROOM_IDENT_RE = /^[#!]/\n\nfunction deriveServerName(userNamespace: string): string {\n // user_namespace is a regex like `@.*:localhost`. The part after the first\n // `:` is the server_name (strip a trailing `)` left over from a wrapped\n // group like `@(.*):localhost)`).\n const tail = userNamespace.split(':').slice(1).join(':').replace(/\\\\?\\)?$/, '')\n if (!tail) throw new Error(`user_namespace missing server_name: ${userNamespace}`)\n return tail\n}\n\nconst TRANSPORT_KINDS = ['matrix', 'http'] as const\ntype TransportKind = (typeof TRANSPORT_KINDS)[number]\n\nfunction parseAcpBlock(name: string, raw: unknown): AcpAgentSpec {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`agents.${name}.acp: must be a mapping with either preset or command`)\n }\n const a = raw as Record<string, unknown>\n const hasPreset = a.preset !== undefined\n const hasCommand = a.command !== undefined\n if (hasPreset && hasCommand) {\n throw new Error(\n `agents.${name}.acp: specify either preset or command, not both`,\n )\n }\n if (!hasPreset && !hasCommand) {\n throw new Error(\n `agents.${name}.acp: must specify either preset or command`,\n )\n }\n if (hasPreset) {\n if (typeof a.preset !== 'string' || a.preset.length === 0) {\n throw new Error(`agents.${name}.acp.preset: must be a non-empty string`)\n }\n if (!isPreset(a.preset)) {\n throw new Error(\n `agents.${name}.acp.preset: unknown preset \"${a.preset}\"`,\n )\n }\n const out: { preset: string; model?: string } = { preset: a.preset }\n if (a.model !== undefined) {\n if (typeof a.model !== 'string' || a.model.trim().length === 0) {\n throw new Error(`agents.${name}.acp.model: must be a non-empty string`)\n }\n out.model = a.model.trim()\n }\n return out as AcpAgentSpec\n }\n if (typeof a.command !== 'string' || a.command.length === 0) {\n throw new Error(`agents.${name}.acp.command: must be a non-empty string`)\n }\n const args: string[] = []\n if (a.args !== undefined) {\n if (!Array.isArray(a.args)) {\n throw new Error(`agents.${name}.acp.args: must be an array of strings`)\n }\n for (const v of a.args) {\n if (typeof v !== 'string') {\n throw new Error(`agents.${name}.acp.args[]: must be a string`)\n }\n args.push(v)\n }\n }\n return { command: a.command, args } as AcpAgentSpec\n}\n\nfunction parseApprovalTimeout(name: string, raw: unknown): number {\n if (raw === undefined) return 0\n if (raw === 0 || raw === '0') return 0\n if (typeof raw !== 'string') {\n throw new Error(\n `agents.${name}.approval_timeout: must be a duration like \"1h\", \"15m\", \"30s\", or 0 to disable (got ${JSON.stringify(raw)})`,\n )\n }\n const m = /^(\\d+)(s|m|h)$/.exec(raw)\n if (!m) {\n throw new Error(\n `agents.${name}.approval_timeout: \"${raw}\" is not a valid duration (use \"<n>s\", \"<n>m\", or \"<n>h\")`,\n )\n }\n const n = Number(m[1])\n switch (m[2]) {\n case 's':\n return n * 1000\n case 'm':\n return n * 60_000\n case 'h':\n return n * 60 * 60_000\n }\n throw new Error('unreachable')\n}\n\nfunction parseAgentContainer(\n name: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): ContainerConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error(`agents.${name}.container must be a mapping`)\n }\n const r = raw as Record<string, unknown>\n const out: ContainerConfig = {}\n if (r.image !== undefined) {\n if (typeof r.image !== 'string' || r.image.length === 0) {\n throw new Error(`agents.${name}.container.image must be a non-empty string`)\n }\n out.image = r.image\n }\n if (r.env !== undefined && r.env !== null) {\n if (typeof r.env !== 'object' || Array.isArray(r.env)) {\n throw new Error(`agents.${name}.container.env must be a mapping`)\n }\n const rawEnv = r.env as Record<string, unknown>\n const stringEnv: Record<string, string> = {}\n for (const [k, v] of Object.entries(rawEnv)) {\n if (typeof v !== 'string') {\n throw new Error(\n `agents.${name}.container.env.${k}: must be a string (got ${typeof v})`,\n )\n }\n stringEnv[k] = v\n }\n out.env = interpolateEnv(stringEnv, processEnv, `agents.${name}.container.env`)\n }\n if (r.mounts !== undefined) {\n out.mounts = parseMountList(name, r.mounts, processEnv, configDir)\n }\n if (r.disable_mounts !== undefined) {\n out.disable_mounts = parseDisableMounts(name, r.disable_mounts)\n }\n return out\n}\n\nfunction parseMountList(\n agentName: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): MountConfig[] {\n if (!Array.isArray(raw)) {\n throw new Error(`agents.${agentName}.container.mounts must be an array`)\n }\n const out: MountConfig[] = []\n const seenIds = new Set<string>()\n for (let i = 0; i < raw.length; i++) {\n const entry = raw[i]\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new Error(`agents.${agentName}.container.mounts[${i}] must be a mapping`)\n }\n const e = entry as Record<string, unknown>\n if (e.host === undefined) {\n throw new Error(`agents.${agentName}.container.mounts[${i}].host is required`)\n }\n if (e.target === undefined) {\n throw new Error(`agents.${agentName}.container.mounts[${i}].target is required`)\n }\n if (typeof e.host !== 'string' || e.host.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].host must be a non-empty string`,\n )\n }\n if (typeof e.target !== 'string' || e.target.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].target must be a non-empty string`,\n )\n }\n const mode = e.mode ?? 'rw'\n if (mode !== 'ro' && mode !== 'rw') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].mode must be \"ro\" or \"rw\" (got ${JSON.stringify(e.mode)})`,\n )\n }\n let id: string | undefined\n if (e.id !== undefined) {\n if (typeof e.id !== 'string' || e.id.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].id must be a non-empty string`,\n )\n }\n if (e.id === 'workspace') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].id: \"workspace\" is a reserved id (set by the workspace auto-mount). Use a different id or rely on disable_mounts to subtract.`,\n )\n }\n if (seenIds.has(e.id)) {\n throw new Error(\n `agents.${agentName}.container.mounts: duplicate id \"${e.id}\"`,\n )\n }\n seenIds.add(e.id)\n id = e.id\n }\n let create: boolean | undefined\n if (e.create !== undefined) {\n if (typeof e.create !== 'boolean') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].create must be a boolean`,\n )\n }\n create = e.create\n }\n const host = resolveHostPath(\n agentName,\n i,\n interpolateString(e.host, processEnv),\n configDir,\n )\n const target = interpolateString(e.target, processEnv)\n const m: MountConfig = { host, target, mode }\n if (id !== undefined) m.id = id\n if (create !== undefined) m.create = create\n out.push(m)\n }\n return out\n}\n\nfunction resolveHostPath(\n agentName: string,\n index: number,\n host: string,\n configDir: string | undefined,\n): string {\n if (host.startsWith('~/')) {\n const home = process.env.HOME\n if (!home) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}].host: cannot expand ~ — $HOME is not set`,\n )\n }\n return `${home}/${host.slice(2)}`\n }\n if (host === '~') {\n const home = process.env.HOME\n if (!home) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}].host: cannot expand ~ — $HOME is not set`,\n )\n }\n return home\n }\n if (isAbsolute(host)) return host\n if (!configDir) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}]: relative host path \"${host}\" requires configDir (zooid.yaml directory) — pass it via loadZooidConfig(yaml, { configDir })`,\n )\n }\n return pathResolve(configDir, host)\n}\n\nfunction parseDisableMounts(agentName: string, raw: unknown): string[] {\n if (!Array.isArray(raw)) {\n throw new Error(`agents.${agentName}.container.disable_mounts must be an array of strings`)\n }\n const out: string[] = []\n for (let i = 0; i < raw.length; i++) {\n const v = raw[i]\n if (typeof v !== 'string' || v.length === 0) {\n throw new Error(\n `agents.${agentName}.container.disable_mounts[${i}] must be a non-empty string`,\n )\n }\n out.push(v)\n }\n return out\n}\n\nfunction parseZooidContainer(raw: unknown): ZooidContainerConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error('container must be a mapping')\n }\n const r = raw as Record<string, unknown>\n const out: ZooidContainerConfig = {}\n if (r.env !== undefined) {\n throw new Error(\n \"Top-level 'container.env' is not supported (workforce-level env defaults are out of scope; see [ZOD043]). \" +\n 'Move env entries to per-agent container.env.',\n )\n }\n if (r.image !== undefined) {\n if (typeof r.image !== 'string' || r.image.length === 0) {\n throw new Error('container.image must be a non-empty string')\n }\n out.image = r.image\n }\n return out\n}\n\nfunction parseTransports(\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n workstation?: string,\n): Record<string, TransportConfig> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('transports: must be a mapping with at least one entry')\n }\n const r = raw as Record<string, unknown>\n const names = Object.keys(r)\n if (names.length === 0) {\n throw new Error('transports: at least one transport must be declared')\n }\n const out: Record<string, TransportConfig> = {}\n for (const name of names) {\n out[name] = parseTransport(name, r[name], processEnv, workstation)\n }\n const matrixEntries = Object.entries(out).filter(\n (e): e is [string, MatrixTransportConfig] => e[1].type === 'matrix',\n )\n if (matrixEntries.length === 1) {\n const [, mt] = matrixEntries[0]!\n if (mt.as_token === '__INFER__') {\n const v = interpolateString('${MATRIX_AS_TOKEN}', processEnv)\n if (v.length === 0) {\n throw new Error(\n 'transports.matrix.as_token: env var MATRIX_AS_TOKEN is not set ' +\n '(set it in your shell or .env, or declare as_token explicitly in zooid.yaml)',\n )\n }\n mt.as_token = v\n }\n if (mt.hs_token === '__INFER__') {\n const v = interpolateString('${MATRIX_HS_TOKEN}', processEnv)\n if (v.length === 0) {\n throw new Error(\n 'transports.matrix.hs_token: env var MATRIX_HS_TOKEN is not set ' +\n '(set it in your shell or .env, or declare hs_token explicitly in zooid.yaml)',\n )\n }\n mt.hs_token = v\n }\n } else if (matrixEntries.length > 1) {\n for (const [tname, mt] of matrixEntries) {\n if (mt.as_token === '__INFER__' || mt.hs_token === '__INFER__') {\n throw new Error(\n `transports.${tname}: as_token / hs_token must be set explicitly when more than one matrix transport is declared ` +\n `(no sensible default env var across multiple transports)`,\n )\n }\n }\n }\n return out\n}\n\nfunction parseTransport(\n name: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n workstation?: string,\n): TransportConfig {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`transports.${name}: must be a mapping`)\n }\n const r = raw as Record<string, unknown>\n const inferredType =\n r.type ?? (name === 'matrix' || name === 'http' ? name : undefined)\n if (inferredType !== 'matrix' && inferredType !== 'http') {\n throw new Error(\n `transports.${name}.type must be \"matrix\" or \"http\" (got ${JSON.stringify(r.type)})`,\n )\n }\n if (inferredType === 'matrix') {\n // Validate mode first so we can give a clear error.\n const mode = r.mode ?? 'appservice'\n if (mode !== 'appservice' && mode !== 'client') {\n throw new Error(\n `transports.${name}.mode must be \"appservice\" or \"client\" (got ${JSON.stringify(r.mode)})`,\n )\n }\n\n // port xor advertise_url\n if (r.port !== undefined && r.advertise_url !== undefined) {\n throw new Error(\n `transports.${name}: set 'port' or 'advertise_url', not both`,\n )\n }\n\n // Workstation-derived defaults: sender_localpart and user_namespace\n let resolvedHost: string | undefined\n if (typeof r.homeserver === 'string') {\n try {\n resolvedHost = new URL(interpolateString(r.homeserver as string, processEnv)).hostname\n } catch {\n // fall through — required-field check below fires the error\n }\n }\n\n if (workstation !== undefined) {\n if (!SLUG_RE.test(workstation)) {\n throw new Error(`workstation must match /^[a-z0-9-]+$/ (got ${JSON.stringify(workstation)})`)\n }\n if (r.sender_localpart === undefined) r.sender_localpart = workstation\n if (r.user_namespace === undefined && resolvedHost) {\n r.user_namespace = `@${workstation}\\\\..*:${resolvedHost}`\n }\n } else {\n if (r.sender_localpart === undefined) r.sender_localpart = 'zooid'\n if (r.user_namespace === undefined && resolvedHost) {\n r.user_namespace = `@.*:${resolvedHost}`\n }\n }\n\n if (r.as_token === undefined) r.as_token = '__INFER__'\n if (r.hs_token === undefined) r.hs_token = '__INFER__'\n const fields = [\n 'homeserver',\n 'as_token',\n 'hs_token',\n 'sender_localpart',\n 'user_namespace',\n ] as const\n for (const f of fields) {\n if (typeof r[f] !== 'string' || (r[f] as string).length === 0) {\n throw new Error(`transports.${name}.${f} must be a non-empty string`)\n }\n }\n const out: MatrixTransportConfig = {\n type: 'matrix',\n homeserver: interpolateString(r.homeserver as string, processEnv),\n as_token: interpolateString(r.as_token as string, processEnv),\n hs_token: interpolateString(r.hs_token as string, processEnv),\n sender_localpart: r.sender_localpart as string,\n user_namespace: r.user_namespace as string,\n mode: mode as 'appservice' | 'client',\n }\n if (workstation !== undefined) out.workstation = workstation\n\n if (r.port !== undefined) {\n if (!Number.isInteger(r.port)) {\n throw new Error(\n `transports.${name}.port must be an integer (got ${JSON.stringify(r.port)})`,\n )\n }\n out.port = r.port as number\n } else if (r.advertise_url !== undefined) {\n if (typeof r.advertise_url !== 'string' || r.advertise_url.length === 0) {\n throw new Error(`transports.${name}.advertise_url must be a non-empty string`)\n }\n out.advertise_url = r.advertise_url as string\n } else {\n // Neither set — default to co-located port\n out.port = 9099\n }\n\n if (r.space !== undefined) {\n if (typeof r.space !== 'string' || r.space.length === 0) {\n throw new Error(\n `transports.${name}.space must be a non-empty string (got ${JSON.stringify(r.space)})`,\n )\n }\n out.space = r.space\n }\n return out\n }\n // type: 'http'\n const port = (r.port ?? 8080) as number\n if (!Number.isInteger(port)) {\n throw new Error(`transports.${name}.port must be an integer (got ${JSON.stringify(port)})`)\n }\n return { type: 'http', port }\n}\n\nfunction parseRoomBinding(path: string, raw: unknown, serverName: string): RoomBinding {\n function normalizeAlias(alias: string): string {\n if (alias.length === 0) {\n throw new Error(`${path}: must be a non-empty alias`)\n }\n if (!MATRIX_ROOM_IDENT_RE.test(alias)) {\n throw new Error(\n `${path}: must start with '#' or '!' (got ${JSON.stringify(alias)})`,\n )\n }\n return alias.includes(':') ? alias : `${alias}:${serverName}`\n }\n if (typeof raw === 'string') {\n return { alias: normalizeAlias(raw) }\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`${path}: must be a string or { alias, power_level } object`)\n }\n const r = raw as Record<string, unknown>\n if (typeof r.alias !== 'string' || r.alias.length === 0) {\n throw new Error(`${path}.alias: must be a non-empty string`)\n }\n const out: RoomBinding = { alias: normalizeAlias(r.alias) }\n if (r.power_level !== undefined) {\n if (typeof r.power_level !== 'number' || !Number.isInteger(r.power_level)) {\n throw new Error(\n `${path}.power_level: must be an integer (got ${JSON.stringify(r.power_level)})`,\n )\n }\n out.powerLevel = r.power_level\n }\n return out\n}\n\nfunction parseTransportBinding(\n name: string,\n entry: Record<string, unknown>,\n transports: Record<string, TransportConfig>,\n): { matrix?: MatrixBinding; http?: HttpBinding } {\n const present = TRANSPORT_KINDS.filter(\n (k) => entry[k] !== undefined && entry[k] !== null,\n )\n if (present.length === 0) {\n throw new Error(\n `agents.${name}: must declare exactly one transport-kind block ` +\n `(e.g. 'matrix:' or 'http:'). Saw none.`,\n )\n }\n if (present.length > 1) {\n throw new Error(\n `agents.${name}: must declare exactly one transport-kind block. ` +\n `Saw: ${present.join(', ')}. To run \"the same agent\" on two transports, ` +\n `declare two agents (e.g. ${name}-matrix and ${name}-http).`,\n )\n }\n const kind = present[0] as TransportKind\n const blockRaw = entry[kind]\n if (typeof blockRaw !== 'object' || blockRaw === null || Array.isArray(blockRaw)) {\n throw new Error(`agents.${name}.${kind}: must be a mapping`)\n }\n const block = blockRaw as Record<string, unknown>\n let refName: string\n if (typeof block.transport === 'string' && block.transport.length > 0) {\n refName = block.transport\n } else {\n const matches = Object.entries(transports).filter(\n ([, t]) => t.type === kind,\n )\n if (matches.length === 0) {\n throw new Error(\n `agents.${name}.${kind}: no transport of type ${kind} declared (add one under transports:)`,\n )\n }\n if (matches.length > 1) {\n throw new Error(\n `agents.${name}.${kind}.transport is required when more than one ${kind} transport is declared ` +\n `(saw: ${matches.map(([n]) => n).join(', ')})`,\n )\n }\n refName = matches[0]![0]\n }\n const refTransport = transports[refName]\n if (!refTransport) {\n throw new Error(\n `agents.${name}.${kind}.transport \"${refName}\" is not declared in transports`,\n )\n }\n if (refTransport.type !== kind) {\n throw new Error(\n `agents.${name}.${kind} references transport \"${refName}\" of type: ${refTransport.type}. ` +\n `Block name and referenced transport's type must match.`,\n )\n }\n\n if (kind === 'matrix') {\n if (refTransport.type !== 'matrix') {\n throw new Error(`agents.${name}.matrix: transport must be matrix`)\n }\n const serverName = deriveServerName(refTransport.user_namespace)\n\n // When the referenced transport is workstationed (ZOD063), an agent with no\n // explicit user_id gets the workstation-scoped MXID @{workstation}.{name},\n // so it lands inside the AS's exclusive @{workstation}\\..* namespace.\n // Without this it would default to @{name} and be rejected (M_EXCLUSIVE).\n const rawUserId =\n typeof block.user_id === 'string' && block.user_id.length > 0\n ? block.user_id\n : refTransport.workstation\n ? `@${refTransport.workstation}.${name}`\n : `@${name}`\n let userId = rawUserId\n if (!userId.includes(':') && MATRIX_USER_LOCALPART_RE.test(userId)) {\n userId = `${userId}:${serverName}`\n }\n if (!MATRIX_USER_ID_RE.test(userId)) {\n throw new Error(\n `agents.${name}.matrix.user_id must look like @localpart:server (got ${JSON.stringify(block.user_id)})`,\n )\n }\n\n if (!Array.isArray(block.rooms) || block.rooms.length === 0) {\n throw new Error(`agents.${name}.matrix.rooms is required and must be a non-empty array`)\n }\n const rooms: RoomBinding[] = []\n for (let i = 0; i < block.rooms.length; i++) {\n rooms.push(parseRoomBinding(`agents.${name}.matrix.rooms[${i}]`, block.rooms[i], serverName))\n }\n\n let displayName: string | undefined\n if (block.display_name !== undefined) {\n if (typeof block.display_name !== 'string') {\n throw new Error(\n `agents.${name}.matrix.display_name must be a string (got ${JSON.stringify(block.display_name)})`,\n )\n }\n const trimmed = block.display_name.trim()\n if (trimmed.length === 0) {\n throw new Error(`agents.${name}.matrix.display_name must be non-empty after trim`)\n }\n if (trimmed.length > 256) {\n throw new Error(`agents.${name}.matrix.display_name must be 256 characters or fewer`)\n }\n displayName = trimmed\n }\n\n const tr = block.trigger ?? 'mention'\n if (tr !== 'mention' && tr !== 'any') {\n throw new Error(\n `agents.${name}.matrix.trigger must be \"mention\" or \"any\" (got ${JSON.stringify(tr)})`,\n )\n }\n const matrix: MatrixBinding = {\n transport: refName,\n user_id: userId,\n rooms,\n trigger: tr,\n }\n if (displayName !== undefined) matrix.display_name = displayName\n return { matrix }\n }\n // kind === 'http'\n return { http: { transport: refName } }\n}\n\nfunction parseAgents(\n raw: unknown,\n runtime: 'local' | 'docker' | 'podman',\n transports: Record<string, TransportConfig>,\n daemonHooks: { pre_turn?: string; post_turn?: string },\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): Record<string, AgentConfig> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('agents: must be a mapping')\n }\n const entries = Object.entries(raw as Record<string, unknown>)\n if (entries.length === 0) {\n throw new Error('agents: must have at least one entry')\n }\n const result: Record<string, AgentConfig> = {}\n for (const [name, val] of entries) {\n if (!AGENT_NAME_RE.test(name)) {\n throw new Error(`agents.${name}: name must match /^[a-z][a-z0-9-]{0,31}$/`)\n }\n if (!val || typeof val !== 'object' || Array.isArray(val)) {\n throw new Error(`agents.${name} must be a mapping`)\n }\n const entry = val as Record<string, unknown>\n let workdir: string\n if (entry.workdir === undefined) {\n workdir = `./agents/${name}`\n } else if (typeof entry.workdir !== 'string' || entry.workdir.length === 0) {\n throw new Error(`agents.${name}.workdir must be a non-empty string`)\n } else {\n workdir = entry.workdir\n }\n\n if (entry.adapter !== undefined) {\n throw new Error(\n `agents.${name}: \"adapter\" is no longer supported; use \"acp\" — see epics/003-ZOD025-acp-migration/SPEC.md`,\n )\n }\n\n if (entry.acp === undefined) {\n throw new Error(`agents.${name}: missing required \"acp\" block`)\n }\n const acp = parseAcpBlock(name, entry.acp)\n const approval_timeout_ms = parseApprovalTimeout(name, entry.approval_timeout)\n\n // Reject legacy fields up front with pointers to [ZOD043].\n if (entry.docker !== undefined) {\n throw new Error(\n `agents.${name}.docker is no longer supported. ` +\n `Move 'image' to agents.${name}.container.image, and 'forward_env' entries to ` +\n `agents.${name}.container.env with \\${VAR} interpolation. See [ZOD043].`,\n )\n }\n if (typeof entry.transport === 'string') {\n throw new Error(\n `agents.${name}.transport (string) is no longer supported at the agent level. ` +\n `Move it inside a transport-kind block, e.g.:\\n` +\n ` matrix:\\n transport: <name>\\n user_id: \"@...\"\\n rooms: [...]\\n` +\n `See [ZOD043].`,\n )\n }\n for (const k of ['matrix_user_id', 'rooms', 'trigger'] as const) {\n if (entry[k] !== undefined) {\n throw new Error(\n `agents.${name}.${k} is no longer supported as a flat field. ` +\n `Move it inside a 'matrix:' block on the agent. See [ZOD043].`,\n )\n }\n }\n\n const agentHooks: AgentConfig['hooks'] = {}\n if (daemonHooks.pre_turn !== undefined) agentHooks.pre_turn = daemonHooks.pre_turn\n if (daemonHooks.post_turn !== undefined) agentHooks.post_turn = daemonHooks.post_turn\n if (entry.hooks !== undefined && entry.hooks !== null) {\n if (typeof entry.hooks !== 'object' || Array.isArray(entry.hooks)) {\n throw new Error(`agents.${name}.hooks must be a mapping`)\n }\n const h = entry.hooks as Record<string, unknown>\n if (Object.prototype.hasOwnProperty.call(h, 'pre_turn')) {\n if (typeof h.pre_turn === 'string') agentHooks.pre_turn = h.pre_turn\n else delete agentHooks.pre_turn\n }\n if (Object.prototype.hasOwnProperty.call(h, 'post_turn')) {\n if (typeof h.post_turn === 'string') agentHooks.post_turn = h.post_turn\n else delete agentHooks.post_turn\n }\n }\n\n let containerBlock: ContainerConfig | undefined\n if (entry.container !== undefined && entry.container !== null) {\n if (runtime === 'local') {\n // Under runtime: local, the parser only accepts mounts/disable_mounts\n // (which the compose layer ignores). image/env stay rejected because\n // they would silently lie: there's no container and the host inherits\n // the daemon's full process.env regardless.\n if (typeof entry.container !== 'object' || entry.container === null || Array.isArray(entry.container)) {\n throw new Error(`agents.${name}.container must be a mapping`)\n }\n const c = entry.container as Record<string, unknown>\n const disallowed = Object.keys(c).filter((k) => k !== 'mounts' && k !== 'disable_mounts')\n if (disallowed.length > 0) {\n throw new Error(\n `agents.${name}.container.${disallowed[0]} is only valid when runtime is 'docker' or 'podman'. ` +\n `runtime: local spawns agents as host child processes — there is no container, ` +\n `so 'image' is inert and 'env' would silently lie (the agent inherits the daemon's ` +\n `full process.env regardless). 'mounts' and 'disable_mounts' are accepted under ` +\n `runtime: local but ignored at compose time.`,\n )\n }\n }\n containerBlock = parseAgentContainer(name, entry.container, processEnv, configDir)\n }\n\n const binding = parseTransportBinding(name, entry, transports)\n\n const agentCfg: AgentConfig = {\n name,\n workdir,\n hooks: agentHooks,\n acp,\n approval_timeout_ms,\n }\n if (containerBlock) agentCfg.container = containerBlock\n if (binding.matrix) agentCfg.matrix = binding.matrix\n if (binding.http) agentCfg.http = binding.http\n result[name] = agentCfg\n }\n return result\n}\n\nfunction parseRuntime(raw: unknown): 'local' | 'docker' | 'podman' {\n const runtime = raw ?? 'docker'\n if (runtime !== 'local' && runtime !== 'docker' && runtime !== 'podman') {\n throw new Error(`runtime must be \"local\", \"docker\", or \"podman\" (got \"${runtime}\")`)\n }\n return runtime\n}\n\nfunction zooidHooks(raw: Record<string, unknown>): { pre_turn?: string; post_turn?: string } {\n const out: { pre_turn?: string; post_turn?: string } = {}\n if (raw.hooks && typeof raw.hooks === 'object') {\n const h = raw.hooks as Record<string, unknown>\n if (typeof h.pre_turn === 'string') out.pre_turn = h.pre_turn\n if (typeof h.post_turn === 'string') out.post_turn = h.post_turn\n }\n return out\n}\n\nexport function loadZooidConfig(\n yamlText: string,\n opts: LoadZooidConfigOptions = {},\n): ZooidConfig {\n const raw = parse(yamlText) ?? {}\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error('zooid.yaml must be a YAML object')\n }\n const r = raw as Record<string, unknown>\n\n if (r.transport !== undefined) {\n throw new Error(\n 'zooid.yaml: top-level \"transport:\" is no longer supported; declare entries under \"transports:\" instead',\n )\n }\n if (r.matrix !== undefined) {\n throw new Error(\n 'zooid.yaml: top-level \"matrix:\" is no longer supported; move it under \"transports.<name>: { type: matrix, ... }\"',\n )\n }\n if (r.workdir !== undefined) {\n throw new Error(\n 'top-level workdir is not supported; define agents: { <name>: { workdir: ... } } instead',\n )\n }\n if (r.docker !== undefined) {\n throw new Error(\n \"Top-level 'docker' block is no longer supported. \" +\n \"Move 'image' to top-level 'container.image'. See [ZOD043].\",\n )\n }\n if (r.agents === undefined) {\n throw new Error('agents: is required — zooid.yaml must define at least one agent')\n }\n\n const runtime = parseRuntime(r.runtime)\n const processEnv = process.env\n\n let workstation: string | undefined\n if (r.workstation !== undefined) {\n if (typeof r.workstation !== 'string' || r.workstation.length === 0) {\n throw new Error('workstation must be a non-empty string')\n }\n if (!SLUG_RE.test(r.workstation)) {\n throw new Error(`workstation must match /^[a-z0-9-]+$/ (got ${JSON.stringify(r.workstation)})`)\n }\n workstation = r.workstation\n }\n\n const transports = parseTransports(r.transports, processEnv, workstation)\n const hooks = zooidHooks(r)\n const agents = parseAgents(r.agents, runtime, transports, hooks, processEnv, opts.configDir)\n\n const cfg: ZooidConfig = {\n runtime,\n transports,\n agents,\n hooks,\n }\n if (workstation !== undefined) cfg.workstation = workstation\n if (r.container !== undefined && r.container !== null) {\n if (runtime === 'local') {\n throw new Error(\n \"container is only valid when runtime is 'docker' or 'podman'. \" +\n 'runtime: local does not run agents in containers; image is ignored. See [ZOD043].',\n )\n }\n cfg.container = parseZooidContainer(r.container)\n }\n return cfg\n}\n\nexport function findTransport(\n cfg: ZooidConfig,\n name: string,\n): TransportConfig | undefined {\n return cfg.transports[name]\n}\n\nexport function findMatrixTransport(\n cfg: ZooidConfig,\n): { name: string; transport: MatrixTransportConfig } | null {\n const matrices = Object.entries(cfg.transports).filter(\n (e): e is [string, MatrixTransportConfig] => e[1].type === 'matrix',\n )\n if (matrices.length === 0) return null\n if (matrices.length > 1) {\n throw new Error(\n `findMatrixTransport: multiple matrix transports declared (${matrices\n .map((m) => m[0])\n .join(', ')}). Per-agent matrix routing is not supported yet.`,\n )\n }\n const [name, transport] = matrices[0]!\n return { name, transport }\n}\n\nexport function findHttpTransport(\n cfg: ZooidConfig,\n): { name: string; transport: HttpTransportConfig } | null {\n const https = Object.entries(cfg.transports).filter(\n (e): e is [string, HttpTransportConfig] => e[1].type === 'http',\n )\n if (https.length === 0) return null\n if (https.length > 1) {\n throw new Error(\n `findHttpTransport: multiple http transports declared (${https\n .map((h) => h[0])\n .join(', ')}). Per-agent http routing is not supported yet.`,\n )\n }\n const [name, transport] = https[0]!\n return { name, transport }\n}\n\nexport interface FoundConfigFile {\n path: string\n}\n\nexport function findConfigFile(cwd: string): FoundConfigFile | null {\n const z = join(cwd, 'zooid.yaml')\n if (existsSync(z)) return { path: z }\n const legacy = join(cwd, 'workforce.yaml')\n if (existsSync(legacy)) {\n throw new Error(\n `workforce.yaml is no longer supported. Rename it to zooid.yaml. See [ZOD045].`,\n )\n }\n return null\n}\n\nexport function mergeCliFlags(base: ZooidConfig, flags: CliFlags): ZooidConfig {\n const runtimeFlag = flags.runtime as 'local' | 'docker' | 'podman' | undefined\n if (\n runtimeFlag !== undefined &&\n runtimeFlag !== 'local' &&\n runtimeFlag !== 'docker' &&\n runtimeFlag !== 'podman'\n ) {\n throw new Error(`runtime must be \"local\", \"docker\", or \"podman\" (got \"${flags.runtime}\")`)\n }\n const runtime = runtimeFlag ?? base.runtime\n const merged: ZooidConfig = {\n runtime,\n transports: base.transports,\n agents: base.agents,\n hooks: { ...base.hooks },\n }\n if (runtime === 'docker' || runtime === 'podman') {\n const image = flags.image ?? base.container?.image\n if (image !== undefined) {\n merged.container = { image }\n } else if (base.container !== undefined) {\n merged.container = { ...base.container }\n }\n }\n return merged\n}\n","import dotenvExpand from 'dotenv-expand'\n\n/**\n * Matches compose-style env references inside a value string:\n * `${NAME}`, `$NAME`, `${NAME:-default}`, `${NAME-default}`,\n * `${NAME:+alt}`, `${NAME+alt}`.\n *\n * The captured group is always the referenced variable name.\n */\nconst REF_RE =\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::?[-+][^}]*)?\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g\n\nexport class EnvInterpolationError extends Error {}\n\nconst isDenied = (name: string): boolean =>\n name === 'ZOOID_TOKEN' || name.startsWith('ZOOID_')\n\n/**\n * Run compose-style interpolation over a `{ KEY: literal-or-${ref} }` map.\n *\n * - Rejects keys in the `ZOOID_*` namespace.\n * - Rejects any `${ZOOID_*}` reference, including inside composed values\n * (e.g. `\"prefix-${ZOOID_INTERNAL}\"`).\n * - Otherwise delegates to `dotenv-expand` for the actual substitution,\n * preserving compose semantics (missing → empty string,\n * `${VAR:-default}`, `${VAR-default}`, `$$` escape).\n */\nexport function interpolateEnv(\n parsed: Record<string, string>,\n processEnv: NodeJS.ProcessEnv,\n scope: string,\n): Record<string, string> {\n for (const [key, val] of Object.entries(parsed)) {\n if (isDenied(key)) {\n throw new EnvInterpolationError(\n `${scope}.${key}: keys in the ZOOID_* namespace are not allowed`,\n )\n }\n if (typeof val !== 'string') {\n throw new EnvInterpolationError(\n `${scope}.${key}: env values must be strings (got ${typeof val})`,\n )\n }\n REF_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = REF_RE.exec(val)) !== null) {\n const ref = (m[1] ?? m[2]) as string\n if (isDenied(ref)) {\n throw new EnvInterpolationError(\n `${scope}.${key}: references to ZOOID_* vars are not allowed (saw \\${${ref}})`,\n )\n }\n }\n }\n // Process each value through interpolateString. Per-key processing avoids\n // dotenv-expand's \"processEnv shadows parsed\" behaviour, which would let the\n // daemon's `LOG_LEVEL=debug` overwrite a literal `LOG_LEVEL: info` declared in\n // zooid.yaml. References still resolve against the full processEnv.\n const out: Record<string, string> = {}\n for (const [key, val] of Object.entries(parsed)) {\n out[key] = interpolateString(val, processEnv)\n }\n return out\n}\n\n/**\n * Run compose-style interpolation over a single string value (e.g. a\n * transport's `as_token`). No denylist — transports legitimately reference\n * `ZOOID_*` tokens for the daemon itself (see [ZOD043] §Denylist note).\n */\nexport function interpolateString(\n value: string,\n processEnv: NodeJS.ProcessEnv,\n): string {\n // Fast path: literals with no `$` need no expansion. Skipping the dotenv-expand\n // call also avoids the library's \"processEnv shadows parsed\" merge behaviour,\n // which can leak unrelated env vars through a sentinel key.\n if (!value.includes('$')) return value\n const sentinel = '__zooid_interp_v1__'\n const env: Record<string, string> = {}\n for (const [k, v] of Object.entries(processEnv)) {\n if (typeof v === 'string') env[k] = v\n }\n delete env[sentinel]\n const result = dotenvExpand.expand({\n parsed: { [sentinel]: value },\n processEnv: env,\n })\n return result.parsed?.[sentinel] ?? ''\n}\n","import { mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n AcpClient,\n resolvePreset,\n type AgentEvent,\n type ApprovalDecision,\n type ApprovalRequest,\n type PromptInput,\n type PromptResult,\n type TapEvent,\n} from '@zooid/acp-client'\nimport type { AcpAgentSpec, AcpMount, AcpRuntime } from './acp-types.js'\nimport type { AgentConfig } from './types.js'\nimport type {\n ApprovalCorrelator,\n RegisteredApproval,\n} from './approval-correlator.js'\n\nexport type AcpRegistryEventHandler = (\n agentName: string,\n event: AgentEvent,\n) => void\nexport type AcpRegistryApprovalHandler = (\n agentName: string,\n req: ApprovalRequest,\n) => Promise<ApprovalDecision>\n\n/**\n * Daemon-side surface of the ACP agent fleet. The transport (HTTP) consumes\n * this; the CLI builds it via `buildAcpRegistry`. Long-lived: one\n * `AcpClient` per agent, kept alive across prompts.\n */\nexport interface AcpRegistry {\n hasAgent(name: string): boolean\n /** Whether an agent has a transport-context provider attached. */\n hasContextSpawn(name: string): boolean\n /** Per-agent approval timeout from zooid.yaml. 0 means no timeout. */\n getApprovalTimeoutMs(name: string): number\n ensureSession(\n name: string,\n threadId: string,\n channelId?: string,\n contextThreadId?: string,\n ): Promise<string>\n /** Drop the in-memory session for (agent, threadId). Next prompt re-creates one. */\n endSession(name: string, threadId: string): void\n prompt(name: string, input: PromptInput): Promise<PromptResult>\n /**\n * Cancel an in-flight prompt for (agent, sessionId). Sends `session/cancel`\n * via the underlying AcpClient and resolves any pending approvals with\n * `decision: 'cancel'`. Idempotent.\n */\n cancelSession(name: string, sessionId: string): Promise<void>\n stopAll(): Promise<void>\n /** Set by the transport. Receives every ACP event from any agent. */\n onEvent: AcpRegistryEventHandler\n /** Set by the transport. Resolves permission requests. */\n onApprovalRequest: AcpRegistryApprovalHandler\n}\n\nexport interface AcpAgentRegistryOptions {\n runtime: AcpRuntime\n agents: Record<string, AgentConfig>\n /** Per-agent env passed to each `AcpClient`'s spawn spec. */\n env?: Record<string, Record<string, string>>\n /** Per-agent container image. Used by DockerAcpRuntime; ignored by LocalAcpRuntime. */\n image?: Record<string, string | undefined>\n /** Initial event handler (the transport may overwrite at app creation). */\n onEvent?: AcpRegistryEventHandler\n /** Initial approval handler (the transport may overwrite at app creation). */\n onApprovalRequest?: AcpRegistryApprovalHandler\n /**\n * Optional correlator: when set, the registry's default\n * `onApprovalRequest` registers each request on the correlator (with the\n * agent's `approval_timeout_ms`) and returns the registered handle's\n * `decisionPromise`. Transports listen on the correlator's `'registered'`\n * + `'timeout'` events to drive the SSE wire and accept HTTP decisions.\n */\n approvals?: ApprovalCorrelator\n /** Called whenever the correlator-backed handler registers an approval. */\n onApprovalRegistered?: (approval: RegisteredApproval) => void\n /**\n * Optional observability tap. Forwarded to each AcpClient so the\n * unfiltered ACP protocol stream + turn-boundary events are visible to\n * the host (e.g. the dev CLI capturing them to disk).\n */\n onTap?: (agentName: string, event: TapEvent) => void\n /**\n * Root directory under which each agent gets a per-agent state dir\n * (`<agentsDir>/<agentName>/`). Used by the AcpClient session store to\n * persist ACP `sessionId`s across daemon restarts. Optional: when unset,\n * session continuity across restarts is disabled.\n */\n agentsDir?: string\n /**\n * Per-agent factory that returns a `mcpServers[]` entry for the\n * `zooid-context` MCP server. Forwarded to each AcpClient. Agents bound to\n * transports without a context provider (e.g. HTTP) have no entry here.\n */\n contextSpawns?: Record<string, ContextSpawnFactory | undefined>\n /**\n * Per-agent resolved bind-mount list. Threaded into the AcpClient's spawn\n * spec; honoured by the docker runtime, ignored by the local runtime.\n */\n mounts?: Record<string, AcpMount[]>\n /**\n * Per-agent list of host directories to `mkdir -p` before the first\n * `runtime.spawn` for that agent. Subset of mount entries with `create: true`.\n */\n mkdirOnSpawn?: Record<string, string[]>\n /**\n * Per-agent override for the spawn-spec `cwd`. Set to e.g. `/workspace`\n * when the workspace mount is active; falls back to `agent.workdir`.\n */\n cwd?: Record<string, string>\n}\n\nexport type ContextSpawnFactory = (\n threadId: string,\n channelId?: string,\n) => Promise<{\n name: 'zooid-context'\n command: string\n args: string[]\n env: Array<{ name: string; value: string }>\n}>\n\nexport class AcpAgentRegistry implements AcpRegistry {\n readonly opts: AcpAgentRegistryOptions\n private readonly clients = new Map<string, AcpClient>()\n\n onEvent: AcpRegistryEventHandler\n onApprovalRequest: AcpRegistryApprovalHandler\n\n constructor(opts: AcpAgentRegistryOptions) {\n this.opts = opts\n this.onEvent = opts.onEvent ?? (() => {})\n if (opts.onApprovalRequest) {\n this.onApprovalRequest = opts.onApprovalRequest\n } else if (opts.approvals) {\n const correlator = opts.approvals\n this.onApprovalRequest = async (name, req) => {\n const cfg = this.opts.agents[name]\n const handle = correlator.register(name, req.sessionId, req, {\n timeoutMs: cfg?.approval_timeout_ms ?? 0,\n })\n this.opts.onApprovalRegistered?.(handle)\n return handle.decisionPromise\n }\n } else {\n this.onApprovalRequest = async () => ({ decision: 'cancel' })\n }\n }\n\n hasAgent(name: string): boolean {\n return Object.prototype.hasOwnProperty.call(this.opts.agents, name)\n }\n\n hasContextSpawn(name: string): boolean {\n return Boolean(this.opts.contextSpawns?.[name])\n }\n\n resolveSpawnEnv(name: string): Record<string, string> {\n return this.opts.env?.[name] ?? {}\n }\n\n resolveSpawnImage(name: string): string | undefined {\n return this.opts.image?.[name]\n }\n\n resolveSpawnMounts(name: string): AcpMount[] {\n return this.opts.mounts?.[name] ?? []\n }\n\n resolveSpawnCwd(name: string): string {\n return (\n this.opts.cwd?.[name] ??\n this.opts.agents[name]?.workdir ??\n process.cwd()\n )\n }\n\n agentNames(): string[] {\n return Object.keys(this.opts.agents)\n }\n\n getApprovalTimeoutMs(name: string): number {\n return this.opts.agents[name]?.approval_timeout_ms ?? 0\n }\n\n async ensureSession(\n name: string,\n threadId: string,\n channelId?: string,\n contextThreadId?: string,\n ): Promise<string> {\n if (!this.hasAgent(name)) throw new Error(`unknown agent: ${name}`)\n const client = await this.ensureClient(name)\n return client.ensureSession(threadId, channelId, contextThreadId)\n }\n\n endSession(name: string, threadId: string): void {\n if (!this.hasAgent(name)) return\n const client = this.clients.get(name)\n client?.endSession(threadId)\n }\n\n async cancelSession(name: string, sessionId: string): Promise<void> {\n if (!this.hasAgent(name)) return\n const client = this.clients.get(name)\n // Always nudge the correlator first so any pending approvals resolve with\n // 'cancel' regardless of whether the client is alive or already stopped.\n this.opts.approvals?.cancelSession(sessionId)\n if (!client) return\n try {\n await client.cancel(sessionId)\n } catch (err) {\n // ACP cancel is a notification; failures here are typically transport\n // errors after the agent has already exited.\n console.warn(`[acp:${name}] cancel(${sessionId}) failed:`, err)\n }\n }\n\n async prompt(name: string, input: PromptInput): Promise<PromptResult> {\n if (!this.hasAgent(name)) throw new Error(`unknown agent: ${name}`)\n const client = await this.ensureClient(name)\n return client.prompt(input)\n }\n\n async stopAll(): Promise<void> {\n await Promise.allSettled(\n [...this.clients.values()].map((c) => c.stop()),\n )\n this.clients.clear()\n }\n\n private async ensureClient(name: string): Promise<AcpClient> {\n const existing = this.clients.get(name)\n if (existing) return existing\n const cfg = this.opts.agents[name]\n if (!cfg.acp) throw new Error(`agents.${name}: missing acp block`)\n const spawn = resolveAcpAgentSpec(cfg.acp)\n for (const dir of this.opts.mkdirOnSpawn?.[name] ?? []) {\n mkdirSync(dir, { recursive: true })\n }\n const client = new AcpClient({\n agent: {\n id: name,\n command: spawn.command,\n args: spawn.args,\n env: this.opts.env?.[name],\n cwd: this.resolveSpawnCwd(name),\n image: this.opts.image?.[name],\n mounts: this.resolveSpawnMounts(name),\n },\n agentDataDir: this.opts.agentsDir ? join(this.opts.agentsDir, name) : undefined,\n runtime: this.opts.runtime,\n onEvent: (e) => this.onEvent(name, e),\n onApprovalRequest: (req) => this.onApprovalRequest(name, req),\n onTap: this.opts.onTap ? (e) => this.opts.onTap!(name, e) : undefined,\n contextSpawn: this.opts.contextSpawns?.[name],\n })\n await client.start()\n this.clients.set(name, client)\n return client\n }\n}\n\nexport function resolveAcpAgentSpec(spec: AcpAgentSpec): {\n command: string\n args: string[]\n} {\n if ('preset' in spec && spec.preset) {\n return resolvePreset(spec.preset, { model: spec.model })\n }\n if ('command' in spec && spec.command) {\n return { command: spec.command, args: spec.args ?? [] }\n }\n throw new Error('AcpAgentSpec: must specify either preset or command')\n}\n","import { EventEmitter } from 'node:events'\nimport { randomUUID } from 'node:crypto'\nimport type {\n ApprovalDecision,\n ApprovalRequest,\n} from '@zooid/acp-client'\n\nexport interface RegisteredApproval {\n approvalId: string\n agentName: string\n sessionId: string\n toolCallId: string\n toolKind?: string\n toolTitle?: string\n toolInput?: unknown\n options: ApprovalRequest['options']\n decisionPromise: Promise<ApprovalDecision>\n}\n\nexport interface RegisterOptions {\n /** Wall-clock timeout in ms. 0 = no timeout. */\n timeoutMs?: number\n}\n\ninterface PendingEntry extends RegisteredApproval {\n resolve(decision: ApprovalDecision): void\n timer?: ReturnType<typeof setTimeout>\n}\n\n/**\n * Correlates ACP approval requests (mid-prompt, originating in `AcpClient`)\n * with HTTP/transport-side decisions. The transport listens to:\n *\n * - `'registered'` — fires after `register()` so the transport can emit\n * `approval.request` on the right SSE stream.\n * - `'timeout'` — fires when an entry is auto-cancelled by the timer\n * so the transport can emit `approval.timeout`.\n */\nexport class ApprovalCorrelator extends EventEmitter {\n private readonly pending = new Map<string, PendingEntry>()\n private readonly bySession = new Map<string, Set<string>>()\n\n register(\n agentName: string,\n sessionId: string,\n req: ApprovalRequest,\n opts: RegisterOptions = {},\n ): RegisteredApproval {\n const approvalId = randomUUID()\n let resolve!: (d: ApprovalDecision) => void\n const decisionPromise = new Promise<ApprovalDecision>((r) => {\n resolve = r\n })\n const entry: PendingEntry = {\n approvalId,\n agentName,\n sessionId,\n toolCallId: req.toolCallId,\n toolKind: req.toolKind,\n toolTitle: req.toolTitle,\n toolInput: req.toolInput,\n options: req.options,\n decisionPromise,\n resolve,\n }\n if (opts.timeoutMs && opts.timeoutMs > 0) {\n entry.timer = setTimeout(() => {\n if (this.pending.get(approvalId) !== entry) return\n entry.resolve({ decision: 'cancel' })\n this.pending.delete(approvalId)\n this.bySession.get(sessionId)?.delete(approvalId)\n this.emit('timeout', { approvalId, sessionId, agentName })\n }, opts.timeoutMs)\n entry.timer.unref?.()\n }\n this.pending.set(approvalId, entry)\n let set = this.bySession.get(sessionId)\n if (!set) {\n set = new Set()\n this.bySession.set(sessionId, set)\n }\n set.add(approvalId)\n this.emit('registered', this.toPublic(entry))\n return this.toPublic(entry)\n }\n\n resolve(\n sessionId: string,\n approvalId: string,\n decision: ApprovalDecision,\n ): boolean {\n const entry = this.pending.get(approvalId)\n if (!entry || entry.sessionId !== sessionId) return false\n if (entry.timer) clearTimeout(entry.timer)\n entry.resolve(decision)\n this.pending.delete(approvalId)\n this.bySession.get(sessionId)?.delete(approvalId)\n return true\n }\n\n cancelSession(sessionId: string): void {\n const ids = this.bySession.get(sessionId)\n if (!ids) return\n for (const id of [...ids]) {\n const entry = this.pending.get(id)\n if (entry) {\n if (entry.timer) clearTimeout(entry.timer)\n entry.resolve({ decision: 'cancel' })\n this.pending.delete(id)\n }\n }\n this.bySession.delete(sessionId)\n }\n\n listPending(sessionId: string): RegisteredApproval[] {\n const ids = this.bySession.get(sessionId)\n if (!ids) return []\n const out: RegisteredApproval[] = []\n for (const id of ids) {\n const entry = this.pending.get(id)\n if (entry) out.push(this.toPublic(entry))\n }\n return out\n }\n\n size(): number {\n return this.pending.size\n }\n\n private toPublic(entry: PendingEntry): RegisteredApproval {\n return {\n approvalId: entry.approvalId,\n agentName: entry.agentName,\n sessionId: entry.sessionId,\n toolCallId: entry.toolCallId,\n toolKind: entry.toolKind,\n toolTitle: entry.toolTitle,\n toolInput: entry.toolInput,\n options: entry.options,\n decisionPromise: entry.decisionPromise,\n }\n }\n}\n"],"mappings":"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","names":["join"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/env-interpolation.ts","../src/match-expression.ts","../src/render-template.ts","../src/acp-registry.ts","../src/approval-correlator.ts","../src/task-actions.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { isAbsolute, join, resolve as pathResolve } from 'node:path'\nimport { parse } from 'yaml'\nimport type { AcpAgentSpec } from './acp-types.js'\nimport { isPreset } from '@zooid/acp-client'\nimport { interpolateEnv, interpolateString } from './env-interpolation.js'\nimport { compileMatch } from './match-expression.js'\nimport type {\n AgentConfig,\n CliFlags,\n ContainerConfig,\n HttpBinding,\n HttpTransportConfig,\n MatrixBinding,\n MatrixTransportConfig,\n MountConfig,\n RoomBinding,\n TransportConfig,\n TriggerConfig,\n TriggerMessage,\n WebhookTriggerConfig,\n ZooidConfig,\n ZooidContainerConfig,\n} from './types.js'\n\nconst WEBHOOK_PROVIDERS = ['github', 'stripe', 'slack', 'standard', 'custom'] as const\n\nconst SLUG_RE = /^[a-z0-9-]+$/\n\nexport interface LoadZooidConfigOptions {\n /**\n * Directory containing zooid.yaml. Required when any agent uses a\n * relative `container.mounts[].host` path; resolution happens at parse\n * time so the resulting `MountConfig` always carries an absolute host\n * path.\n */\n configDir?: string\n /**\n * Cron-expression validator, called as `validateCron(name, expr)` and\n * expected to throw on an invalid expression. `core` takes no cron\n * dependency — `cli` passes croner's parser at load time. Defaults to a\n * field-count check sufficient to catch malformed input at parse time.\n */\n validateCron?: (name: string, expr: string) => void\n}\n\nconst AGENT_NAME_RE = /^[a-z][a-z0-9-]{0,31}$/\nconst MATRIX_USER_ID_RE = /^@[A-Za-z0-9._\\-=/+]+:[A-Za-z0-9.\\-]+$/\nconst MATRIX_USER_LOCALPART_RE = /^@[a-z0-9._=/+\\-]+$/\nconst MATRIX_ROOM_IDENT_RE = /^[#!]/\n\nfunction deriveServerName(userNamespace: string): string {\n // user_namespace is a regex like `@.*:localhost`. The part after the first\n // `:` is the server_name (strip a trailing `)` left over from a wrapped\n // group like `@(.*):localhost)`).\n const tail = userNamespace.split(':').slice(1).join(':').replace(/\\\\?\\)?$/, '')\n if (!tail) throw new Error(`user_namespace missing server_name: ${userNamespace}`)\n return tail\n}\n\nconst TRANSPORT_KINDS = ['matrix', 'http'] as const\ntype TransportKind = (typeof TRANSPORT_KINDS)[number]\n\nfunction parseAcpBlock(name: string, raw: unknown): AcpAgentSpec {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`agents.${name}.acp: must be a mapping with either preset or command`)\n }\n const a = raw as Record<string, unknown>\n const hasPreset = a.preset !== undefined\n const hasCommand = a.command !== undefined\n if (hasPreset && hasCommand) {\n throw new Error(\n `agents.${name}.acp: specify either preset or command, not both`,\n )\n }\n if (!hasPreset && !hasCommand) {\n throw new Error(\n `agents.${name}.acp: must specify either preset or command`,\n )\n }\n if (hasPreset) {\n if (typeof a.preset !== 'string' || a.preset.length === 0) {\n throw new Error(`agents.${name}.acp.preset: must be a non-empty string`)\n }\n if (!isPreset(a.preset)) {\n throw new Error(\n `agents.${name}.acp.preset: unknown preset \"${a.preset}\"`,\n )\n }\n const out: { preset: string; model?: string } = { preset: a.preset }\n if (a.model !== undefined) {\n if (typeof a.model !== 'string' || a.model.trim().length === 0) {\n throw new Error(`agents.${name}.acp.model: must be a non-empty string`)\n }\n out.model = a.model.trim()\n }\n return out as AcpAgentSpec\n }\n if (typeof a.command !== 'string' || a.command.length === 0) {\n throw new Error(`agents.${name}.acp.command: must be a non-empty string`)\n }\n const args: string[] = []\n if (a.args !== undefined) {\n if (!Array.isArray(a.args)) {\n throw new Error(`agents.${name}.acp.args: must be an array of strings`)\n }\n for (const v of a.args) {\n if (typeof v !== 'string') {\n throw new Error(`agents.${name}.acp.args[]: must be a string`)\n }\n args.push(v)\n }\n }\n return { command: a.command, args } as AcpAgentSpec\n}\n\nfunction parseApprovalTimeout(name: string, raw: unknown): number {\n if (raw === undefined) return 0\n if (raw === 0 || raw === '0') return 0\n if (typeof raw !== 'string') {\n throw new Error(\n `agents.${name}.approval_timeout: must be a duration like \"1h\", \"15m\", \"30s\", or 0 to disable (got ${JSON.stringify(raw)})`,\n )\n }\n const m = /^(\\d+)(s|m|h)$/.exec(raw)\n if (!m) {\n throw new Error(\n `agents.${name}.approval_timeout: \"${raw}\" is not a valid duration (use \"<n>s\", \"<n>m\", or \"<n>h\")`,\n )\n }\n const n = Number(m[1])\n switch (m[2]) {\n case 's':\n return n * 1000\n case 'm':\n return n * 60_000\n case 'h':\n return n * 60 * 60_000\n }\n throw new Error('unreachable')\n}\n\nfunction parseAgentContainer(\n name: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): ContainerConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error(`agents.${name}.container must be a mapping`)\n }\n const r = raw as Record<string, unknown>\n const out: ContainerConfig = {}\n if (r.image !== undefined) {\n if (typeof r.image !== 'string' || r.image.length === 0) {\n throw new Error(`agents.${name}.container.image must be a non-empty string`)\n }\n out.image = r.image\n }\n if (r.env !== undefined && r.env !== null) {\n if (typeof r.env !== 'object' || Array.isArray(r.env)) {\n throw new Error(`agents.${name}.container.env must be a mapping`)\n }\n const rawEnv = r.env as Record<string, unknown>\n const stringEnv: Record<string, string> = {}\n for (const [k, v] of Object.entries(rawEnv)) {\n if (typeof v !== 'string') {\n throw new Error(\n `agents.${name}.container.env.${k}: must be a string (got ${typeof v})`,\n )\n }\n stringEnv[k] = v\n }\n out.env = interpolateEnv(stringEnv, processEnv, `agents.${name}.container.env`)\n }\n if (r.mounts !== undefined) {\n out.mounts = parseMountList(name, r.mounts, processEnv, configDir)\n }\n if (r.disable_mounts !== undefined) {\n out.disable_mounts = parseDisableMounts(name, r.disable_mounts)\n }\n return out\n}\n\nfunction parseMountList(\n agentName: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): MountConfig[] {\n if (!Array.isArray(raw)) {\n throw new Error(`agents.${agentName}.container.mounts must be an array`)\n }\n const out: MountConfig[] = []\n const seenIds = new Set<string>()\n for (let i = 0; i < raw.length; i++) {\n const entry = raw[i]\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {\n throw new Error(`agents.${agentName}.container.mounts[${i}] must be a mapping`)\n }\n const e = entry as Record<string, unknown>\n if (e.host === undefined) {\n throw new Error(`agents.${agentName}.container.mounts[${i}].host is required`)\n }\n if (e.target === undefined) {\n throw new Error(`agents.${agentName}.container.mounts[${i}].target is required`)\n }\n if (typeof e.host !== 'string' || e.host.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].host must be a non-empty string`,\n )\n }\n if (typeof e.target !== 'string' || e.target.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].target must be a non-empty string`,\n )\n }\n const mode = e.mode ?? 'rw'\n if (mode !== 'ro' && mode !== 'rw') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].mode must be \"ro\" or \"rw\" (got ${JSON.stringify(e.mode)})`,\n )\n }\n let id: string | undefined\n if (e.id !== undefined) {\n if (typeof e.id !== 'string' || e.id.length === 0) {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].id must be a non-empty string`,\n )\n }\n if (e.id === 'workspace') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].id: \"workspace\" is a reserved id (set by the workspace auto-mount). Use a different id or rely on disable_mounts to subtract.`,\n )\n }\n if (seenIds.has(e.id)) {\n throw new Error(\n `agents.${agentName}.container.mounts: duplicate id \"${e.id}\"`,\n )\n }\n seenIds.add(e.id)\n id = e.id\n }\n let create: boolean | undefined\n if (e.create !== undefined) {\n if (typeof e.create !== 'boolean') {\n throw new Error(\n `agents.${agentName}.container.mounts[${i}].create must be a boolean`,\n )\n }\n create = e.create\n }\n const host = resolveHostPath(\n agentName,\n i,\n interpolateString(e.host, processEnv),\n configDir,\n )\n const target = interpolateString(e.target, processEnv)\n const m: MountConfig = { host, target, mode }\n if (id !== undefined) m.id = id\n if (create !== undefined) m.create = create\n out.push(m)\n }\n return out\n}\n\nfunction resolveHostPath(\n agentName: string,\n index: number,\n host: string,\n configDir: string | undefined,\n): string {\n if (host.startsWith('~/')) {\n const home = process.env.HOME\n if (!home) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}].host: cannot expand ~ — $HOME is not set`,\n )\n }\n return `${home}/${host.slice(2)}`\n }\n if (host === '~') {\n const home = process.env.HOME\n if (!home) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}].host: cannot expand ~ — $HOME is not set`,\n )\n }\n return home\n }\n if (isAbsolute(host)) return host\n if (!configDir) {\n throw new Error(\n `agents.${agentName}.container.mounts[${index}]: relative host path \"${host}\" requires configDir (zooid.yaml directory) — pass it via loadZooidConfig(yaml, { configDir })`,\n )\n }\n return pathResolve(configDir, host)\n}\n\nfunction parseDisableMounts(agentName: string, raw: unknown): string[] {\n if (!Array.isArray(raw)) {\n throw new Error(`agents.${agentName}.container.disable_mounts must be an array of strings`)\n }\n const out: string[] = []\n for (let i = 0; i < raw.length; i++) {\n const v = raw[i]\n if (typeof v !== 'string' || v.length === 0) {\n throw new Error(\n `agents.${agentName}.container.disable_mounts[${i}] must be a non-empty string`,\n )\n }\n out.push(v)\n }\n return out\n}\n\nfunction parseZooidContainer(raw: unknown): ZooidContainerConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error('container must be a mapping')\n }\n const r = raw as Record<string, unknown>\n const out: ZooidContainerConfig = {}\n if (r.env !== undefined) {\n throw new Error(\n \"Top-level 'container.env' is not supported (workforce-level env defaults are out of scope; see [ZOD043]). \" +\n 'Move env entries to per-agent container.env.',\n )\n }\n if (r.image !== undefined) {\n if (typeof r.image !== 'string' || r.image.length === 0) {\n throw new Error('container.image must be a non-empty string')\n }\n out.image = r.image\n }\n return out\n}\n\nfunction parseTransports(\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n workstation?: string,\n): Record<string, TransportConfig> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('transports: must be a mapping with at least one entry')\n }\n const r = raw as Record<string, unknown>\n const names = Object.keys(r)\n if (names.length === 0) {\n throw new Error('transports: at least one transport must be declared')\n }\n const out: Record<string, TransportConfig> = {}\n for (const name of names) {\n out[name] = parseTransport(name, r[name], processEnv, workstation)\n }\n const matrixEntries = Object.entries(out).filter(\n (e): e is [string, MatrixTransportConfig] => e[1].type === 'matrix',\n )\n if (matrixEntries.length === 1) {\n const [, mt] = matrixEntries[0]!\n if (mt.as_token === '__INFER__') {\n const v = interpolateString('${MATRIX_AS_TOKEN}', processEnv)\n if (v.length === 0) {\n throw new Error(\n 'transports.matrix.as_token: env var MATRIX_AS_TOKEN is not set ' +\n '(set it in your shell or .env, or declare as_token explicitly in zooid.yaml)',\n )\n }\n mt.as_token = v\n }\n if (mt.hs_token === '__INFER__') {\n const v = interpolateString('${MATRIX_HS_TOKEN}', processEnv)\n if (v.length === 0) {\n throw new Error(\n 'transports.matrix.hs_token: env var MATRIX_HS_TOKEN is not set ' +\n '(set it in your shell or .env, or declare hs_token explicitly in zooid.yaml)',\n )\n }\n mt.hs_token = v\n }\n } else if (matrixEntries.length > 1) {\n for (const [tname, mt] of matrixEntries) {\n if (mt.as_token === '__INFER__' || mt.hs_token === '__INFER__') {\n throw new Error(\n `transports.${tname}: as_token / hs_token must be set explicitly when more than one matrix transport is declared ` +\n `(no sensible default env var across multiple transports)`,\n )\n }\n }\n }\n return out\n}\n\nfunction parseTransport(\n name: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n workstation?: string,\n): TransportConfig {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`transports.${name}: must be a mapping`)\n }\n const r = raw as Record<string, unknown>\n const inferredType =\n r.type ?? (name === 'matrix' || name === 'http' ? name : undefined)\n if (inferredType !== 'matrix' && inferredType !== 'http') {\n throw new Error(\n `transports.${name}.type must be \"matrix\" or \"http\" (got ${JSON.stringify(r.type)})`,\n )\n }\n if (inferredType === 'matrix') {\n // Validate mode first so we can give a clear error.\n const mode = r.mode ?? 'appservice'\n if (mode !== 'appservice' && mode !== 'client') {\n throw new Error(\n `transports.${name}.mode must be \"appservice\" or \"client\" (got ${JSON.stringify(r.mode)})`,\n )\n }\n\n // port xor advertise_url\n if (r.port !== undefined && r.advertise_url !== undefined) {\n throw new Error(\n `transports.${name}: set 'port' or 'advertise_url', not both`,\n )\n }\n\n // Workstation-derived defaults: sender_localpart and user_namespace\n let resolvedHost: string | undefined\n if (typeof r.homeserver === 'string') {\n try {\n resolvedHost = new URL(interpolateString(r.homeserver as string, processEnv)).hostname\n } catch {\n // fall through — required-field check below fires the error\n }\n }\n\n if (workstation !== undefined) {\n if (!SLUG_RE.test(workstation)) {\n throw new Error(`workstation must match /^[a-z0-9-]+$/ (got ${JSON.stringify(workstation)})`)\n }\n if (r.sender_localpart === undefined) r.sender_localpart = workstation\n if (r.user_namespace === undefined && resolvedHost) {\n r.user_namespace = `@${workstation}\\\\..*:${resolvedHost}`\n }\n } else {\n if (r.sender_localpart === undefined) r.sender_localpart = 'zooid'\n if (r.user_namespace === undefined && resolvedHost) {\n r.user_namespace = `@.*:${resolvedHost}`\n }\n }\n\n if (r.as_token === undefined) r.as_token = '__INFER__'\n if (r.hs_token === undefined) r.hs_token = '__INFER__'\n const fields = [\n 'homeserver',\n 'as_token',\n 'hs_token',\n 'sender_localpart',\n 'user_namespace',\n ] as const\n for (const f of fields) {\n if (typeof r[f] !== 'string' || (r[f] as string).length === 0) {\n throw new Error(`transports.${name}.${f} must be a non-empty string`)\n }\n }\n const out: MatrixTransportConfig = {\n type: 'matrix',\n homeserver: interpolateString(r.homeserver as string, processEnv),\n as_token: interpolateString(r.as_token as string, processEnv),\n hs_token: interpolateString(r.hs_token as string, processEnv),\n sender_localpart: r.sender_localpart as string,\n user_namespace: r.user_namespace as string,\n mode: mode as 'appservice' | 'client',\n }\n if (workstation !== undefined) out.workstation = workstation\n\n if (r.port !== undefined) {\n if (!Number.isInteger(r.port)) {\n throw new Error(\n `transports.${name}.port must be an integer (got ${JSON.stringify(r.port)})`,\n )\n }\n out.port = r.port as number\n } else if (r.advertise_url !== undefined) {\n if (typeof r.advertise_url !== 'string' || r.advertise_url.length === 0) {\n throw new Error(`transports.${name}.advertise_url must be a non-empty string`)\n }\n out.advertise_url = r.advertise_url as string\n } else {\n // Neither set — default to co-located port\n out.port = 9099\n }\n\n if (r.space !== undefined) {\n if (typeof r.space !== 'string' || r.space.length === 0) {\n throw new Error(\n `transports.${name}.space must be a non-empty string (got ${JSON.stringify(r.space)})`,\n )\n }\n out.space = r.space\n }\n return out\n }\n // type: 'http'\n const port = (r.port ?? 8080) as number\n if (!Number.isInteger(port)) {\n throw new Error(`transports.${name}.port must be an integer (got ${JSON.stringify(port)})`)\n }\n return { type: 'http', port }\n}\n\nfunction parseRoomBinding(path: string, raw: unknown, serverName: string): RoomBinding {\n function normalizeAlias(alias: string): string {\n if (alias.length === 0) {\n throw new Error(`${path}: must be a non-empty alias`)\n }\n if (!MATRIX_ROOM_IDENT_RE.test(alias)) {\n throw new Error(\n `${path}: must start with '#' or '!' (got ${JSON.stringify(alias)})`,\n )\n }\n return alias.includes(':') ? alias : `${alias}:${serverName}`\n }\n if (typeof raw === 'string') {\n return { alias: normalizeAlias(raw) }\n }\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`${path}: must be a string or { alias, power_level } object`)\n }\n const r = raw as Record<string, unknown>\n if (typeof r.alias !== 'string' || r.alias.length === 0) {\n throw new Error(`${path}.alias: must be a non-empty string`)\n }\n const out: RoomBinding = { alias: normalizeAlias(r.alias) }\n if (r.power_level !== undefined) {\n if (typeof r.power_level !== 'number' || !Number.isInteger(r.power_level)) {\n throw new Error(\n `${path}.power_level: must be an integer (got ${JSON.stringify(r.power_level)})`,\n )\n }\n out.powerLevel = r.power_level\n }\n return out\n}\n\nfunction parseTransportBinding(\n name: string,\n entry: Record<string, unknown>,\n transports: Record<string, TransportConfig>,\n): { matrix?: MatrixBinding; http?: HttpBinding } {\n const present = TRANSPORT_KINDS.filter(\n (k) => entry[k] !== undefined && entry[k] !== null,\n )\n if (present.length === 0) {\n throw new Error(\n `agents.${name}: must declare exactly one transport-kind block ` +\n `(e.g. 'matrix:' or 'http:'). Saw none.`,\n )\n }\n if (present.length > 1) {\n throw new Error(\n `agents.${name}: must declare exactly one transport-kind block. ` +\n `Saw: ${present.join(', ')}. To run \"the same agent\" on two transports, ` +\n `declare two agents (e.g. ${name}-matrix and ${name}-http).`,\n )\n }\n const kind = present[0] as TransportKind\n const blockRaw = entry[kind]\n if (typeof blockRaw !== 'object' || blockRaw === null || Array.isArray(blockRaw)) {\n throw new Error(`agents.${name}.${kind}: must be a mapping`)\n }\n const block = blockRaw as Record<string, unknown>\n let refName: string\n if (typeof block.transport === 'string' && block.transport.length > 0) {\n refName = block.transport\n } else {\n const matches = Object.entries(transports).filter(\n ([, t]) => t.type === kind,\n )\n if (matches.length === 0) {\n throw new Error(\n `agents.${name}.${kind}: no transport of type ${kind} declared (add one under transports:)`,\n )\n }\n if (matches.length > 1) {\n throw new Error(\n `agents.${name}.${kind}.transport is required when more than one ${kind} transport is declared ` +\n `(saw: ${matches.map(([n]) => n).join(', ')})`,\n )\n }\n refName = matches[0]![0]\n }\n const refTransport = transports[refName]\n if (!refTransport) {\n throw new Error(\n `agents.${name}.${kind}.transport \"${refName}\" is not declared in transports`,\n )\n }\n if (refTransport.type !== kind) {\n throw new Error(\n `agents.${name}.${kind} references transport \"${refName}\" of type: ${refTransport.type}. ` +\n `Block name and referenced transport's type must match.`,\n )\n }\n\n if (kind === 'matrix') {\n if (refTransport.type !== 'matrix') {\n throw new Error(`agents.${name}.matrix: transport must be matrix`)\n }\n const serverName = deriveServerName(refTransport.user_namespace)\n\n // When the referenced transport is workstationed (ZOD063), an agent with no\n // explicit user_id gets the workstation-scoped MXID @{workstation}.{name},\n // so it lands inside the AS's exclusive @{workstation}\\..* namespace.\n // Without this it would default to @{name} and be rejected (M_EXCLUSIVE).\n const rawUserId =\n typeof block.user_id === 'string' && block.user_id.length > 0\n ? block.user_id\n : refTransport.workstation\n ? `@${refTransport.workstation}.${name}`\n : `@${name}`\n let userId = rawUserId\n if (!userId.includes(':') && MATRIX_USER_LOCALPART_RE.test(userId)) {\n userId = `${userId}:${serverName}`\n }\n if (!MATRIX_USER_ID_RE.test(userId)) {\n throw new Error(\n `agents.${name}.matrix.user_id must look like @localpart:server (got ${JSON.stringify(block.user_id)})`,\n )\n }\n\n if (!Array.isArray(block.rooms) || block.rooms.length === 0) {\n throw new Error(`agents.${name}.matrix.rooms is required and must be a non-empty array`)\n }\n const rooms: RoomBinding[] = []\n for (let i = 0; i < block.rooms.length; i++) {\n rooms.push(parseRoomBinding(`agents.${name}.matrix.rooms[${i}]`, block.rooms[i], serverName))\n }\n\n let displayName: string | undefined\n if (block.display_name !== undefined) {\n if (typeof block.display_name !== 'string') {\n throw new Error(\n `agents.${name}.matrix.display_name must be a string (got ${JSON.stringify(block.display_name)})`,\n )\n }\n const trimmed = block.display_name.trim()\n if (trimmed.length === 0) {\n throw new Error(`agents.${name}.matrix.display_name must be non-empty after trim`)\n }\n if (trimmed.length > 256) {\n throw new Error(`agents.${name}.matrix.display_name must be 256 characters or fewer`)\n }\n displayName = trimmed\n }\n\n const tr = block.trigger ?? 'mention'\n if (tr !== 'mention' && tr !== 'any') {\n throw new Error(\n `agents.${name}.matrix.trigger must be \"mention\" or \"any\" (got ${JSON.stringify(tr)})`,\n )\n }\n const matrix: MatrixBinding = {\n transport: refName,\n user_id: userId,\n rooms,\n trigger: tr,\n }\n if (displayName !== undefined) matrix.display_name = displayName\n return { matrix }\n }\n // kind === 'http'\n return { http: { transport: refName } }\n}\n\nfunction parseAgents(\n raw: unknown,\n runtime: 'local' | 'docker' | 'podman',\n transports: Record<string, TransportConfig>,\n daemonHooks: { pre_turn?: string; post_turn?: string },\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): Record<string, AgentConfig> {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('agents: must be a mapping')\n }\n const entries = Object.entries(raw as Record<string, unknown>)\n if (entries.length === 0) {\n throw new Error('agents: must have at least one entry')\n }\n const result: Record<string, AgentConfig> = {}\n for (const [name, val] of entries) {\n if (!AGENT_NAME_RE.test(name)) {\n throw new Error(`agents.${name}: name must match /^[a-z][a-z0-9-]{0,31}$/`)\n }\n if (!val || typeof val !== 'object' || Array.isArray(val)) {\n throw new Error(`agents.${name} must be a mapping`)\n }\n const entry = val as Record<string, unknown>\n let workdir: string\n if (entry.workdir === undefined) {\n workdir = `./agents/${name}`\n } else if (typeof entry.workdir !== 'string' || entry.workdir.length === 0) {\n throw new Error(`agents.${name}.workdir must be a non-empty string`)\n } else {\n workdir = entry.workdir\n }\n\n if (entry.adapter !== undefined) {\n throw new Error(\n `agents.${name}: \"adapter\" is no longer supported; use \"acp\" — see epics/003-ZOD025-acp-migration/SPEC.md`,\n )\n }\n\n if (entry.acp === undefined) {\n throw new Error(`agents.${name}: missing required \"acp\" block`)\n }\n const acp = parseAcpBlock(name, entry.acp)\n const approval_timeout_ms = parseApprovalTimeout(name, entry.approval_timeout)\n\n // Reject legacy fields up front with pointers to [ZOD043].\n if (entry.docker !== undefined) {\n throw new Error(\n `agents.${name}.docker is no longer supported. ` +\n `Move 'image' to agents.${name}.container.image, and 'forward_env' entries to ` +\n `agents.${name}.container.env with \\${VAR} interpolation. See [ZOD043].`,\n )\n }\n if (typeof entry.transport === 'string') {\n throw new Error(\n `agents.${name}.transport (string) is no longer supported at the agent level. ` +\n `Move it inside a transport-kind block, e.g.:\\n` +\n ` matrix:\\n transport: <name>\\n user_id: \"@...\"\\n rooms: [...]\\n` +\n `See [ZOD043].`,\n )\n }\n for (const k of ['matrix_user_id', 'rooms', 'trigger'] as const) {\n if (entry[k] !== undefined) {\n throw new Error(\n `agents.${name}.${k} is no longer supported as a flat field. ` +\n `Move it inside a 'matrix:' block on the agent. See [ZOD043].`,\n )\n }\n }\n\n const agentHooks: AgentConfig['hooks'] = {}\n if (daemonHooks.pre_turn !== undefined) agentHooks.pre_turn = daemonHooks.pre_turn\n if (daemonHooks.post_turn !== undefined) agentHooks.post_turn = daemonHooks.post_turn\n if (entry.hooks !== undefined && entry.hooks !== null) {\n if (typeof entry.hooks !== 'object' || Array.isArray(entry.hooks)) {\n throw new Error(`agents.${name}.hooks must be a mapping`)\n }\n const h = entry.hooks as Record<string, unknown>\n if (Object.prototype.hasOwnProperty.call(h, 'pre_turn')) {\n if (typeof h.pre_turn === 'string') agentHooks.pre_turn = h.pre_turn\n else delete agentHooks.pre_turn\n }\n if (Object.prototype.hasOwnProperty.call(h, 'post_turn')) {\n if (typeof h.post_turn === 'string') agentHooks.post_turn = h.post_turn\n else delete agentHooks.post_turn\n }\n }\n\n let containerBlock: ContainerConfig | undefined\n if (entry.container !== undefined && entry.container !== null) {\n if (runtime === 'local') {\n // Under runtime: local, the parser only accepts mounts/disable_mounts\n // (which the compose layer ignores). image/env stay rejected because\n // they would silently lie: there's no container and the host inherits\n // the daemon's full process.env regardless.\n if (typeof entry.container !== 'object' || entry.container === null || Array.isArray(entry.container)) {\n throw new Error(`agents.${name}.container must be a mapping`)\n }\n const c = entry.container as Record<string, unknown>\n const disallowed = Object.keys(c).filter((k) => k !== 'mounts' && k !== 'disable_mounts')\n if (disallowed.length > 0) {\n throw new Error(\n `agents.${name}.container.${disallowed[0]} is only valid when runtime is 'docker' or 'podman'. ` +\n `runtime: local spawns agents as host child processes — there is no container, ` +\n `so 'image' is inert and 'env' would silently lie (the agent inherits the daemon's ` +\n `full process.env regardless). 'mounts' and 'disable_mounts' are accepted under ` +\n `runtime: local but ignored at compose time.`,\n )\n }\n }\n containerBlock = parseAgentContainer(name, entry.container, processEnv, configDir)\n }\n\n const binding = parseTransportBinding(name, entry, transports)\n\n const agentCfg: AgentConfig = {\n name,\n workdir,\n hooks: agentHooks,\n acp,\n approval_timeout_ms,\n }\n if (containerBlock) agentCfg.container = containerBlock\n if (binding.matrix) agentCfg.matrix = binding.matrix\n if (binding.http) agentCfg.http = binding.http\n result[name] = agentCfg\n }\n return result\n}\n\nfunction defaultValidateCron(name: string, expr: string): void {\n const parts = expr.trim().split(/\\s+/)\n if (parts.length < 5 || parts.length > 7) {\n throw new Error(\n `triggers.${name}.schedule: invalid cron expression ${JSON.stringify(expr)}`,\n )\n }\n}\n\n/**\n * Expand a trigger's `as:` to a full MXID. Mirrors the explicit-`user_id`\n * path in `parseTransportBinding`: a value already containing `:` is used\n * as given (and validated); a bare localpart (`cron` or `@cron`) gets\n * `:<server>` appended from the workforce's sole matrix transport. Unlike\n * the *default* user_id (omitted case), this never auto-prefixes a\n * workstation — an explicit value is used as written, so a\n * workstation-scoped bot is written explicitly (`as: myworkstation.cron`).\n */\nfunction expandTriggerAs(\n name: string,\n as: string,\n transports: Record<string, TransportConfig>,\n): string {\n if (as.includes(':')) {\n if (!MATRIX_USER_ID_RE.test(as)) {\n throw new Error(`triggers.${name}.as: must be a full MXID (got ${JSON.stringify(as)})`)\n }\n return as\n }\n const bare = as.startsWith('@') ? as : `@${as}`\n if (!MATRIX_USER_LOCALPART_RE.test(bare)) {\n throw new Error(`triggers.${name}.as: must be a full MXID (got ${JSON.stringify(as)})`)\n }\n const matrixTransports = Object.values(transports).filter(\n (t): t is MatrixTransportConfig => t.type === 'matrix',\n )\n if (matrixTransports.length !== 1) {\n throw new Error(\n `triggers.${name}.as: \"${as}\" is a bare localpart, which requires exactly one matrix ` +\n `transport to expand against (found ${matrixTransports.length}). Use a full MXID instead.`,\n )\n }\n const serverName = deriveServerName(matrixTransports[0]!.user_namespace)\n return `${bare}:${serverName}`\n}\n\nfunction parseTriggers(\n raw: unknown,\n agents: Record<string, AgentConfig>,\n transports: Record<string, TransportConfig>,\n validateCron: (name: string, expr: string) => void,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): Record<string, TriggerConfig> {\n if (raw === undefined || raw === null) return {}\n if (typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('triggers: must be a mapping')\n }\n const result: Record<string, TriggerConfig> = {}\n for (const [name, val] of Object.entries(raw as Record<string, unknown>)) {\n if (!val || typeof val !== 'object' || Array.isArray(val)) {\n throw new Error(`triggers.${name} must be a mapping`)\n }\n const t = val as Record<string, unknown>\n\n if (t.run !== undefined) {\n throw new Error(\n `triggers.${name}.run: is not supported — a trigger posts a message and the ` +\n `agent runs what needs running. See [ZOD081] §Concept.`,\n )\n }\n\n if (t.schedule === undefined && t.webhook === undefined) {\n throw new Error(`triggers.${name}: must specify schedule: or webhook:`)\n }\n if (t.schedule !== undefined && t.webhook !== undefined) {\n throw new Error(`triggers.${name}: specify either schedule: or webhook:, not both`)\n }\n\n let schedule: string | undefined\n if (t.schedule !== undefined) {\n if (typeof t.schedule !== 'string' || t.schedule.length === 0) {\n throw new Error(`triggers.${name}.schedule: must be a non-empty string`)\n }\n validateCron(name, t.schedule)\n schedule = t.schedule\n }\n\n let webhook: WebhookTriggerConfig | undefined\n if (t.webhook !== undefined) {\n webhook = parseWebhookTrigger(name, t.webhook, processEnv, configDir)\n }\n\n if (typeof t.as !== 'string' || t.as.length === 0) {\n throw new Error(`triggers.${name}.as: must be a non-empty string`)\n }\n const as = expandTriggerAs(name, t.as, transports)\n\n const hasFlat = t.room !== undefined || t.mention !== undefined || t.text !== undefined ||\n t.match !== undefined\n const hasMessages = t.messages !== undefined\n if (hasFlat && hasMessages) {\n throw new Error(\n `triggers.${name}: specify either room:/mention:/text: or messages:, not both`,\n )\n }\n if (!hasFlat && !hasMessages) {\n throw new Error(`triggers.${name}: must specify room:/mention:/text: or messages:`)\n }\n\n let rawMessages: unknown[]\n if (hasFlat) {\n // Only a flat trigger's own top-level `match:` gets this unindexed\n // name — once desugared, per-entry validation always speaks in terms\n // of `messages[i]`, whether the entry came from `flat` or `messages:`.\n if (t.match !== undefined && !webhook) {\n throw new Error(`triggers.${name}.match: only applies to a webhook: trigger`)\n }\n rawMessages = [{ room: t.room, mention: t.mention, text: t.text, match: t.match }]\n } else {\n if (!Array.isArray(t.messages)) {\n throw new Error(`triggers.${name}.messages: must be a list`)\n }\n if (t.messages.length === 0) {\n throw new Error(`triggers.${name}.messages: must not be empty`)\n }\n rawMessages = t.messages\n }\n\n const messages = rawMessages.map((m, i) =>\n parseTriggerMessage(name, i, m, agents, as, !!webhook),\n )\n\n const entry: TriggerConfig = { as, messages }\n if (schedule !== undefined) entry.schedule = schedule\n if (webhook !== undefined) entry.webhook = webhook\n result[name] = entry\n }\n return result\n}\n\n/**\n * Parse and validate one entry of `messages:` (or the single entry a flat\n * trigger desugars into). Indexed error names (`triggers.<name>.messages[i]`)\n * apply uniformly here regardless of which spelling produced the entry —\n * only the flat-level `match:` presence check (in `parseTriggers`) still\n * speaks in the unindexed, trigger-level name.\n */\nfunction parseTriggerMessage(\n name: string,\n index: number,\n raw: unknown,\n agents: Record<string, AgentConfig>,\n as: string,\n hasWebhook: boolean,\n): TriggerMessage {\n const label = `triggers.${name}.messages[${index}]`\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`${label}: must be a mapping`)\n }\n const m = raw as Record<string, unknown>\n\n if (typeof m.room !== 'string' || m.room.length === 0) {\n throw new Error(`${label}.room: must be a non-empty string`)\n }\n if (!MATRIX_ROOM_IDENT_RE.test(m.room)) {\n throw new Error(`${label}.room: must start with '#' or '!' (got ${JSON.stringify(m.room)})`)\n }\n\n if (typeof m.mention !== 'string' || m.mention.length === 0) {\n throw new Error(`${label}.mention: must be a non-empty string`)\n }\n const mentionedAgent = agents[m.mention]\n if (!mentionedAgent) {\n throw new Error(`${label}.mention: unknown agent \"${m.mention}\"`)\n }\n if (!mentionedAgent.matrix) {\n throw new Error(\n `${label}.mention: agent \"${m.mention}\" has no matrix: binding — a trigger posts ` +\n `through Matrix, so the mentioned agent must be matrix-bound.`,\n )\n }\n // router.ts never routes an event back to its own sender\n // (`event.sender === a.userId` short-circuits the match), so a trigger\n // that posts as the very agent it mentions would silently never wake it.\n if (as === mentionedAgent.matrix.user_id) {\n throw new Error(\n `triggers.${name}.as: must not equal the mentioned agent's own MXID (${as}) — a message ` +\n `an agent sends never routes back to itself, so this trigger would silently never wake ` +\n `\"${m.mention}\". Post as a different identity (a dedicated bot, or another agent's).`,\n )\n }\n\n if (typeof m.text !== 'string' || m.text.length === 0) {\n throw new Error(`${label}.text: must be a non-empty string`)\n }\n\n const message: TriggerMessage = { room: m.room, mention: m.mention, text: m.text }\n\n if (m.match !== undefined) {\n if (typeof m.match !== 'string' || m.match.length === 0) {\n throw new Error(`${label}.match: must be a non-empty string`)\n }\n if (!hasWebhook) {\n throw new Error(`${label}.match: only applies to a webhook: trigger`)\n }\n try {\n message.match = compileMatch(m.match)\n } catch (err) {\n throw new Error(`${label}.match: ${(err as Error).message}`)\n }\n }\n\n return message\n}\n\n/**\n * Parse and validate a `webhook:` block. The secret is interpolated through\n * `interpolateString` (not `interpolateEnv`'s deny-listed map form) — it is a\n * single scalar value, and `ZOOID_*` references are legitimate here just as\n * they are for a transport's `as_token` — [[ZOD082]] §Design 4, Secrets.\n */\nfunction parseWebhookTrigger(\n name: string,\n raw: unknown,\n processEnv: NodeJS.ProcessEnv,\n configDir: string | undefined,\n): WebhookTriggerConfig {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error(`triggers.${name}.webhook: must be a mapping`)\n }\n const w = raw as Record<string, unknown>\n\n if (typeof w.provider !== 'string' || w.provider.length === 0) {\n throw new Error(`triggers.${name}.webhook.provider: must be a non-empty string`)\n }\n if (!(WEBHOOK_PROVIDERS as readonly string[]).includes(w.provider)) {\n throw new Error(\n `triggers.${name}.webhook.provider: unknown provider ${JSON.stringify(w.provider)} ` +\n `(expected one of: ${WEBHOOK_PROVIDERS.join(', ')})`,\n )\n }\n const provider = w.provider as WebhookTriggerConfig['provider']\n\n if (w.event !== undefined) {\n throw new Error(`triggers.${name}.webhook.event: no longer supported — use match:`)\n }\n\n if (typeof w.secret !== 'string' || w.secret.length === 0) {\n throw new Error(`triggers.${name}.webhook.secret: is required`)\n }\n const secret = interpolateString(w.secret, processEnv)\n\n const config: WebhookTriggerConfig = { provider, secret }\n\n if (provider !== 'custom') {\n // Silently ignoring this would leave an operator believing they had\n // configured a verifier that never ran.\n if (w.verify !== undefined) {\n throw new Error(\n `triggers.${name}.webhook.verify: only applies to provider: custom ` +\n `(this trigger uses provider: ${provider}, whose signing scheme is built in)`,\n )\n }\n return config\n }\n\n // provider: custom — verification is a function the operator supplies,\n // since no set of declarative fields covers every scheme (ed25519, SHA-1\n // over sorted params, bespoke timestamped base strings). Resolved here;\n // the daemon imports it at startup so a bad path fails fast.\n if (typeof w.verify !== 'string' || w.verify.length === 0) {\n throw new Error(\n `triggers.${name}.webhook.verify: is required when provider: custom ` +\n `(path to a module exporting the verifier function)`,\n )\n }\n if (isAbsolute(w.verify)) {\n config.verify = w.verify\n } else if (!configDir) {\n throw new Error(\n `triggers.${name}.webhook.verify: relative path ${JSON.stringify(w.verify)} requires ` +\n `configDir (zooid.yaml directory) — pass it via loadZooidConfig(yaml, { configDir })`,\n )\n } else {\n config.verify = pathResolve(configDir, w.verify)\n }\n\n return config\n}\n\nfunction parseRuntime(raw: unknown): 'local' | 'docker' | 'podman' {\n const runtime = raw ?? 'docker'\n if (runtime !== 'local' && runtime !== 'docker' && runtime !== 'podman') {\n throw new Error(`runtime must be \"local\", \"docker\", or \"podman\" (got \"${runtime}\")`)\n }\n return runtime\n}\n\nfunction zooidHooks(raw: Record<string, unknown>): { pre_turn?: string; post_turn?: string } {\n const out: { pre_turn?: string; post_turn?: string } = {}\n if (raw.hooks && typeof raw.hooks === 'object') {\n const h = raw.hooks as Record<string, unknown>\n if (typeof h.pre_turn === 'string') out.pre_turn = h.pre_turn\n if (typeof h.post_turn === 'string') out.post_turn = h.post_turn\n }\n return out\n}\n\nexport function loadZooidConfig(\n yamlText: string,\n opts: LoadZooidConfigOptions = {},\n): ZooidConfig {\n const raw = parse(yamlText) ?? {}\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error('zooid.yaml must be a YAML object')\n }\n const r = raw as Record<string, unknown>\n\n if (r.transport !== undefined) {\n throw new Error(\n 'zooid.yaml: top-level \"transport:\" is no longer supported; declare entries under \"transports:\" instead',\n )\n }\n if (r.matrix !== undefined) {\n throw new Error(\n 'zooid.yaml: top-level \"matrix:\" is no longer supported; move it under \"transports.<name>: { type: matrix, ... }\"',\n )\n }\n if (r.workdir !== undefined) {\n throw new Error(\n 'top-level workdir is not supported; define agents: { <name>: { workdir: ... } } instead',\n )\n }\n if (r.docker !== undefined) {\n throw new Error(\n \"Top-level 'docker' block is no longer supported. \" +\n \"Move 'image' to top-level 'container.image'. See [ZOD043].\",\n )\n }\n if (r.agents === undefined) {\n throw new Error('agents: is required — zooid.yaml must define at least one agent')\n }\n\n const runtime = parseRuntime(r.runtime)\n const processEnv = process.env\n\n let workstation: string | undefined\n if (r.workstation !== undefined) {\n if (typeof r.workstation !== 'string' || r.workstation.length === 0) {\n throw new Error('workstation must be a non-empty string')\n }\n if (!SLUG_RE.test(r.workstation)) {\n throw new Error(`workstation must match /^[a-z0-9-]+$/ (got ${JSON.stringify(r.workstation)})`)\n }\n workstation = r.workstation\n }\n\n const transports = parseTransports(r.transports, processEnv, workstation)\n const hooks = zooidHooks(r)\n const agents = parseAgents(r.agents, runtime, transports, hooks, processEnv, opts.configDir)\n const triggers = parseTriggers(\n r.triggers,\n agents,\n transports,\n opts.validateCron ?? defaultValidateCron,\n processEnv,\n opts.configDir,\n )\n\n const cfg: ZooidConfig = {\n runtime,\n transports,\n agents,\n hooks,\n triggers,\n }\n if (workstation !== undefined) cfg.workstation = workstation\n if (r.container !== undefined && r.container !== null) {\n if (runtime === 'local') {\n throw new Error(\n \"container is only valid when runtime is 'docker' or 'podman'. \" +\n 'runtime: local does not run agents in containers; image is ignored. See [ZOD043].',\n )\n }\n cfg.container = parseZooidContainer(r.container)\n }\n return cfg\n}\n\nexport function findTransport(\n cfg: ZooidConfig,\n name: string,\n): TransportConfig | undefined {\n return cfg.transports[name]\n}\n\nexport function findMatrixTransport(\n cfg: ZooidConfig,\n): { name: string; transport: MatrixTransportConfig } | null {\n const matrices = Object.entries(cfg.transports).filter(\n (e): e is [string, MatrixTransportConfig] => e[1].type === 'matrix',\n )\n if (matrices.length === 0) return null\n if (matrices.length > 1) {\n throw new Error(\n `findMatrixTransport: multiple matrix transports declared (${matrices\n .map((m) => m[0])\n .join(', ')}). Per-agent matrix routing is not supported yet.`,\n )\n }\n const [name, transport] = matrices[0]!\n return { name, transport }\n}\n\nexport function findHttpTransport(\n cfg: ZooidConfig,\n): { name: string; transport: HttpTransportConfig } | null {\n const https = Object.entries(cfg.transports).filter(\n (e): e is [string, HttpTransportConfig] => e[1].type === 'http',\n )\n if (https.length === 0) return null\n if (https.length > 1) {\n throw new Error(\n `findHttpTransport: multiple http transports declared (${https\n .map((h) => h[0])\n .join(', ')}). Per-agent http routing is not supported yet.`,\n )\n }\n const [name, transport] = https[0]!\n return { name, transport }\n}\n\nexport interface FoundConfigFile {\n path: string\n}\n\nexport function findConfigFile(cwd: string): FoundConfigFile | null {\n const z = join(cwd, 'zooid.yaml')\n if (existsSync(z)) return { path: z }\n const legacy = join(cwd, 'workforce.yaml')\n if (existsSync(legacy)) {\n throw new Error(\n `workforce.yaml is no longer supported. Rename it to zooid.yaml. See [ZOD045].`,\n )\n }\n return null\n}\n\nexport function mergeCliFlags(base: ZooidConfig, flags: CliFlags): ZooidConfig {\n const runtimeFlag = flags.runtime as 'local' | 'docker' | 'podman' | undefined\n if (\n runtimeFlag !== undefined &&\n runtimeFlag !== 'local' &&\n runtimeFlag !== 'docker' &&\n runtimeFlag !== 'podman'\n ) {\n throw new Error(`runtime must be \"local\", \"docker\", or \"podman\" (got \"${flags.runtime}\")`)\n }\n const runtime = runtimeFlag ?? base.runtime\n const merged: ZooidConfig = {\n runtime,\n transports: base.transports,\n agents: base.agents,\n hooks: { ...base.hooks },\n triggers: base.triggers,\n }\n if (runtime === 'docker' || runtime === 'podman') {\n const image = flags.image ?? base.container?.image\n if (image !== undefined) {\n merged.container = { image }\n } else if (base.container !== undefined) {\n merged.container = { ...base.container }\n }\n }\n return merged\n}\n","import dotenvExpand from 'dotenv-expand'\n\n/**\n * Matches compose-style env references inside a value string:\n * `${NAME}`, `$NAME`, `${NAME:-default}`, `${NAME-default}`,\n * `${NAME:+alt}`, `${NAME+alt}`.\n *\n * The captured group is always the referenced variable name.\n */\nconst REF_RE =\n /\\$\\{([A-Za-z_][A-Za-z0-9_]*)(?::?[-+][^}]*)?\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g\n\nexport class EnvInterpolationError extends Error {}\n\nconst isDenied = (name: string): boolean =>\n name === 'ZOOID_TOKEN' || name.startsWith('ZOOID_')\n\n/**\n * Run compose-style interpolation over a `{ KEY: literal-or-${ref} }` map.\n *\n * - Rejects keys in the `ZOOID_*` namespace.\n * - Rejects any `${ZOOID_*}` reference, including inside composed values\n * (e.g. `\"prefix-${ZOOID_INTERNAL}\"`).\n * - Otherwise delegates to `dotenv-expand` for the actual substitution,\n * preserving compose semantics (missing → empty string,\n * `${VAR:-default}`, `${VAR-default}`, `$$` escape).\n */\nexport function interpolateEnv(\n parsed: Record<string, string>,\n processEnv: NodeJS.ProcessEnv,\n scope: string,\n): Record<string, string> {\n for (const [key, val] of Object.entries(parsed)) {\n if (isDenied(key)) {\n throw new EnvInterpolationError(\n `${scope}.${key}: keys in the ZOOID_* namespace are not allowed`,\n )\n }\n if (typeof val !== 'string') {\n throw new EnvInterpolationError(\n `${scope}.${key}: env values must be strings (got ${typeof val})`,\n )\n }\n REF_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = REF_RE.exec(val)) !== null) {\n const ref = (m[1] ?? m[2]) as string\n if (isDenied(ref)) {\n throw new EnvInterpolationError(\n `${scope}.${key}: references to ZOOID_* vars are not allowed (saw \\${${ref}})`,\n )\n }\n }\n }\n // Process each value through interpolateString. Per-key processing avoids\n // dotenv-expand's \"processEnv shadows parsed\" behaviour, which would let the\n // daemon's `LOG_LEVEL=debug` overwrite a literal `LOG_LEVEL: info` declared in\n // zooid.yaml. References still resolve against the full processEnv.\n const out: Record<string, string> = {}\n for (const [key, val] of Object.entries(parsed)) {\n out[key] = interpolateString(val, processEnv)\n }\n return out\n}\n\n/**\n * Run compose-style interpolation over a single string value (e.g. a\n * transport's `as_token`). No denylist — transports legitimately reference\n * `ZOOID_*` tokens for the daemon itself (see [ZOD043] §Denylist note).\n */\nexport function interpolateString(\n value: string,\n processEnv: NodeJS.ProcessEnv,\n): string {\n // Fast path: literals with no `$` need no expansion. Skipping the dotenv-expand\n // call also avoids the library's \"processEnv shadows parsed\" merge behaviour,\n // which can leak unrelated env vars through a sentinel key.\n if (!value.includes('$')) return value\n const sentinel = '__zooid_interp_v1__'\n const env: Record<string, string> = {}\n for (const [k, v] of Object.entries(processEnv)) {\n if (typeof v === 'string') env[k] = v\n }\n delete env[sentinel]\n const result = dotenvExpand.expand({\n parsed: { [sentinel]: value },\n processEnv: env,\n })\n return result.parsed?.[sentinel] ?? ''\n}\n","import { parse, run, type CelInput } from '@bufbuild/cel'\n\nexport interface MatchContext {\n event: string | undefined\n body: unknown\n headers: Record<string, string>\n output: string\n}\n\n/** Parse-check an expression. Throws on a syntax error, so a typo fails at config load. */\nexport function compileMatch(expr: string): string {\n parse(expr)\n return expr\n}\n\n/**\n * Only an actual `true` fires. Everything else is \"no match\":\n * - a CelError value (missing field, undeclared variable) — the library\n * returns these rather than throwing, and a merged-PR filter legitimately\n * errors on every issues delivery;\n * - a truthy non-boolean (a string, a number), which must never be coerced;\n * - a throw.\n * Failing closed matters more here than anywhere else in the ingress: this is\n * the only place an operator's typo could otherwise open a filter.\n */\nexport function evaluateMatch(expr: string, ctx: MatchContext): boolean {\n try {\n const result = run(expr, { ...ctx } as Record<string, CelInput>)\n return result === true\n } catch {\n return false\n }\n}\n","import { run, isCelError, type CelInput } from '@bufbuild/cel'\nimport type { MatchContext } from './match-expression.js'\n\n// Non-greedy so `${a}...${b}` is two placeholders, not one spanning both.\nconst PLACEHOLDER_RE = /\\$\\{([\\s\\S]+?)\\}/g\n\n/**\n * Fill `${...}` placeholders in a trigger's `text:`. Each placeholder is a\n * CEL expression over the same bindings `match:` sees — one expression\n * language, not two. `${output}` is not a special case: `output` is just\n * another bound variable, so a whole-payload dump still works for senders we\n * control ([[ZOD081]] §2).\n *\n * An unresolvable placeholder (missing field, bad expression) renders as\n * empty rather than throwing or pasting an error object into a room — a bad\n * placeholder must not take the message down.\n *\n * The result is never re-scanned, so a payload that itself contains the\n * literal string `${...}` cannot inject a placeholder.\n */\nexport function renderTemplate(template: string, ctx: MatchContext): string {\n return template.replace(PLACEHOLDER_RE, (_match, expr: string) => {\n let result: unknown\n try {\n result = run(expr, { ...ctx } as Record<string, CelInput>)\n } catch {\n return ''\n }\n if (result === undefined || isCelError(result)) return ''\n return String(result)\n })\n}\n","import { mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport {\n AcpClient,\n resolvePreset,\n type AgentEvent,\n type ApprovalDecision,\n type ApprovalRequest,\n type PromptInput,\n type PromptResult,\n type TapEvent,\n} from '@zooid/acp-client'\nimport type { AcpAgentSpec, AcpMount, AcpRuntime } from './acp-types.js'\nimport type { AgentConfig } from './types.js'\nimport type { ApprovalCorrelator, RegisteredApproval } from './approval-correlator.js'\n\nexport type AcpRegistryEventHandler = (agentName: string, event: AgentEvent) => void\nexport type AcpRegistryApprovalHandler = (\n agentName: string,\n req: ApprovalRequest,\n) => Promise<ApprovalDecision>\n\n/**\n * Daemon-side surface of the ACP agent fleet. The transport (HTTP) consumes\n * this; the CLI builds it via `buildAcpRegistry`. Long-lived: one\n * `AcpClient` per agent, kept alive across prompts.\n */\nexport interface AcpRegistry {\n hasAgent(name: string): boolean\n /** Whether an agent has a transport-context provider attached. */\n hasContextSpawn(name: string): boolean\n /** Per-agent approval timeout from zooid.yaml. 0 means no timeout. */\n getApprovalTimeoutMs(name: string): number\n ensureSession(\n name: string,\n threadId: string,\n channelId?: string,\n contextThreadId?: string,\n ): Promise<string>\n /** Drop the in-memory session for (agent, threadId). Next prompt re-creates one. */\n endSession(name: string, threadId: string): void\n prompt(name: string, input: PromptInput): Promise<PromptResult>\n /**\n * Cancel an in-flight prompt for (agent, sessionId). Sends `session/cancel`\n * via the underlying AcpClient and resolves any pending approvals with\n * `decision: 'cancel'`. Idempotent.\n */\n cancelSession(name: string, sessionId: string): Promise<void>\n stopAll(): Promise<void>\n /** Set by the transport. Receives every ACP event from any agent. */\n onEvent: AcpRegistryEventHandler\n /** Set by the transport. Resolves permission requests. */\n onApprovalRequest: AcpRegistryApprovalHandler\n}\n\nexport interface AcpAgentRegistryOptions {\n runtime: AcpRuntime\n agents: Record<string, AgentConfig>\n /** Per-agent env passed to each `AcpClient`'s spawn spec. */\n env?: Record<string, Record<string, string>>\n /** Per-agent container image. Used by DockerAcpRuntime; ignored by LocalAcpRuntime. */\n image?: Record<string, string | undefined>\n /** Initial event handler (the transport may overwrite at app creation). */\n onEvent?: AcpRegistryEventHandler\n /** Initial approval handler (the transport may overwrite at app creation). */\n onApprovalRequest?: AcpRegistryApprovalHandler\n /**\n * Optional correlator: when set, the registry's default\n * `onApprovalRequest` registers each request on the correlator (with the\n * agent's `approval_timeout_ms`) and returns the registered handle's\n * `decisionPromise`. Transports listen on the correlator's `'registered'`\n * + `'timeout'` events to drive the SSE wire and accept HTTP decisions.\n */\n approvals?: ApprovalCorrelator\n /** Called whenever the correlator-backed handler registers an approval. */\n onApprovalRegistered?: (approval: RegisteredApproval) => void\n /**\n * Optional observability tap. Forwarded to each AcpClient so the\n * unfiltered ACP protocol stream + turn-boundary events are visible to\n * the host (e.g. the dev CLI capturing them to disk).\n */\n onTap?: (agentName: string, event: TapEvent) => void\n /**\n * Root directory under which each agent gets a per-agent state dir\n * (`<agentsDir>/<agentName>/`). Used by the AcpClient session store to\n * persist ACP `sessionId`s across daemon restarts. Optional: when unset,\n * session continuity across restarts is disabled.\n */\n agentsDir?: string\n /**\n * Per-agent factory that returns a `mcpServers[]` entry for the\n * `zooid-context` MCP server. Forwarded to each AcpClient. Agents bound to\n * transports without a context provider (e.g. HTTP) have no entry here.\n */\n contextSpawns?: Record<string, ContextSpawnFactory | undefined>\n /**\n * Per-agent resolved bind-mount list. Threaded into the AcpClient's spawn\n * spec; honoured by the docker runtime, ignored by the local runtime.\n */\n mounts?: Record<string, AcpMount[]>\n /**\n * Per-agent list of host directories to `mkdir -p` before the first\n * `runtime.spawn` for that agent. Subset of mount entries with `create: true`.\n */\n mkdirOnSpawn?: Record<string, string[]>\n /**\n * Per-agent override for the spawn-spec `cwd`. Set to e.g. `/workspace`\n * when the workspace mount is active; falls back to `agent.workdir`.\n */\n cwd?: Record<string, string>\n /**\n * Called after an ACP session is created or recovered. Context adapters\n * which cannot receive an MCP spawn id (Pi) use this to resolve their\n * daemon-side binding by the ACP session id instead.\n */\n onSessionEstablished?: (agentName: string, sessionKey: string, sessionId: string) => void\n}\n\nexport type ContextSpawnFactory = (\n threadId: string,\n channelId?: string,\n sessionKey?: string,\n) => Promise<{\n name: 'zooid-context'\n command: string\n args: string[]\n env: Array<{ name: string; value: string }>\n}>\n\nexport class AcpAgentRegistry implements AcpRegistry {\n readonly opts: AcpAgentRegistryOptions\n private readonly clients = new Map<string, AcpClient>()\n\n onEvent: AcpRegistryEventHandler\n onApprovalRequest: AcpRegistryApprovalHandler\n\n constructor(opts: AcpAgentRegistryOptions) {\n this.opts = opts\n this.onEvent = opts.onEvent ?? (() => {})\n if (opts.onApprovalRequest) {\n this.onApprovalRequest = opts.onApprovalRequest\n } else if (opts.approvals) {\n const correlator = opts.approvals\n this.onApprovalRequest = async (name, req) => {\n const cfg = this.opts.agents[name]\n const handle = correlator.register(name, req.sessionId, req, {\n timeoutMs: cfg?.approval_timeout_ms ?? 0,\n })\n this.opts.onApprovalRegistered?.(handle)\n return handle.decisionPromise\n }\n } else {\n this.onApprovalRequest = async () => ({ decision: 'cancel' })\n }\n }\n\n hasAgent(name: string): boolean {\n return Object.prototype.hasOwnProperty.call(this.opts.agents, name)\n }\n\n hasContextSpawn(name: string): boolean {\n return Boolean(this.opts.contextSpawns?.[name])\n }\n\n resolveSpawnEnv(name: string): Record<string, string> {\n return this.opts.env?.[name] ?? {}\n }\n\n resolveSpawnImage(name: string): string | undefined {\n return this.opts.image?.[name]\n }\n\n resolveSpawnMounts(name: string): AcpMount[] {\n return this.opts.mounts?.[name] ?? []\n }\n\n resolveSpawnCwd(name: string): string {\n return this.opts.cwd?.[name] ?? this.opts.agents[name]?.workdir ?? process.cwd()\n }\n\n agentNames(): string[] {\n return Object.keys(this.opts.agents)\n }\n\n getApprovalTimeoutMs(name: string): number {\n return this.opts.agents[name]?.approval_timeout_ms ?? 0\n }\n\n async ensureSession(\n name: string,\n threadId: string,\n channelId?: string,\n contextThreadId?: string,\n ): Promise<string> {\n if (!this.hasAgent(name)) throw new Error(`unknown agent: ${name}`)\n const client = await this.ensureClient(name)\n const sessionId = await client.ensureSession(threadId, channelId, contextThreadId)\n this.opts.onSessionEstablished?.(name, threadId, sessionId)\n return sessionId\n }\n\n endSession(name: string, threadId: string): void {\n if (!this.hasAgent(name)) return\n const client = this.clients.get(name)\n client?.endSession(threadId)\n }\n\n async cancelSession(name: string, sessionId: string): Promise<void> {\n if (!this.hasAgent(name)) return\n const client = this.clients.get(name)\n // Always nudge the correlator first so any pending approvals resolve with\n // 'cancel' regardless of whether the client is alive or already stopped.\n this.opts.approvals?.cancelSession(sessionId)\n if (!client) return\n try {\n await client.cancel(sessionId)\n } catch (err) {\n // ACP cancel is a notification; failures here are typically transport\n // errors after the agent has already exited.\n console.warn(`[acp:${name}] cancel(${sessionId}) failed:`, err)\n }\n }\n\n async prompt(name: string, input: PromptInput): Promise<PromptResult> {\n if (!this.hasAgent(name)) throw new Error(`unknown agent: ${name}`)\n const client = await this.ensureClient(name)\n return client.prompt(input)\n }\n\n async stopAll(): Promise<void> {\n await Promise.allSettled([...this.clients.values()].map((c) => c.stop()))\n this.clients.clear()\n }\n\n private async ensureClient(name: string): Promise<AcpClient> {\n const existing = this.clients.get(name)\n if (existing) return existing\n const cfg = this.opts.agents[name]\n if (!cfg.acp) throw new Error(`agents.${name}: missing acp block`)\n const spawn = resolveAcpAgentSpec(cfg.acp)\n for (const dir of this.opts.mkdirOnSpawn?.[name] ?? []) {\n mkdirSync(dir, { recursive: true })\n }\n const client = new AcpClient({\n agent: {\n id: name,\n command: spawn.command,\n args: spawn.args,\n env: this.opts.env?.[name],\n cwd: this.resolveSpawnCwd(name),\n image: this.opts.image?.[name],\n mounts: this.resolveSpawnMounts(name),\n },\n agentDataDir: this.opts.agentsDir ? join(this.opts.agentsDir, name) : undefined,\n runtime: this.opts.runtime,\n onEvent: (e) => this.onEvent(name, e),\n onApprovalRequest: (req) => this.onApprovalRequest(name, req),\n onTap: this.opts.onTap ? (e) => this.opts.onTap!(name, e) : undefined,\n contextSpawn: this.opts.contextSpawns?.[name],\n })\n await client.start()\n this.clients.set(name, client)\n return client\n }\n}\n\nexport function resolveAcpAgentSpec(spec: AcpAgentSpec): {\n command: string\n args: string[]\n} {\n if ('preset' in spec && spec.preset) {\n return resolvePreset(spec.preset, { model: spec.model })\n }\n if ('command' in spec && spec.command) {\n return { command: spec.command, args: spec.args ?? [] }\n }\n throw new Error('AcpAgentSpec: must specify either preset or command')\n}\n","import { EventEmitter } from 'node:events'\nimport { randomUUID } from 'node:crypto'\nimport type {\n ApprovalDecision,\n ApprovalRequest,\n} from '@zooid/acp-client'\n\nexport interface RegisteredApproval {\n approvalId: string\n agentName: string\n sessionId: string\n toolCallId: string\n toolKind?: string\n toolTitle?: string\n toolInput?: unknown\n options: ApprovalRequest['options']\n decisionPromise: Promise<ApprovalDecision>\n}\n\nexport interface RegisterOptions {\n /** Wall-clock timeout in ms. 0 = no timeout. */\n timeoutMs?: number\n}\n\ninterface PendingEntry extends RegisteredApproval {\n resolve(decision: ApprovalDecision): void\n timer?: ReturnType<typeof setTimeout>\n}\n\n/**\n * Correlates ACP approval requests (mid-prompt, originating in `AcpClient`)\n * with HTTP/transport-side decisions. The transport listens to:\n *\n * - `'registered'` — fires after `register()` so the transport can emit\n * `approval.request` on the right SSE stream.\n * - `'timeout'` — fires when an entry is auto-cancelled by the timer\n * so the transport can emit `approval.timeout`.\n */\nexport class ApprovalCorrelator extends EventEmitter {\n private readonly pending = new Map<string, PendingEntry>()\n private readonly bySession = new Map<string, Set<string>>()\n\n register(\n agentName: string,\n sessionId: string,\n req: ApprovalRequest,\n opts: RegisterOptions = {},\n ): RegisteredApproval {\n const approvalId = randomUUID()\n let resolve!: (d: ApprovalDecision) => void\n const decisionPromise = new Promise<ApprovalDecision>((r) => {\n resolve = r\n })\n const entry: PendingEntry = {\n approvalId,\n agentName,\n sessionId,\n toolCallId: req.toolCallId,\n toolKind: req.toolKind,\n toolTitle: req.toolTitle,\n toolInput: req.toolInput,\n options: req.options,\n decisionPromise,\n resolve,\n }\n if (opts.timeoutMs && opts.timeoutMs > 0) {\n entry.timer = setTimeout(() => {\n if (this.pending.get(approvalId) !== entry) return\n entry.resolve({ decision: 'cancel' })\n this.pending.delete(approvalId)\n this.bySession.get(sessionId)?.delete(approvalId)\n this.emit('timeout', { approvalId, sessionId, agentName })\n }, opts.timeoutMs)\n entry.timer.unref?.()\n }\n this.pending.set(approvalId, entry)\n let set = this.bySession.get(sessionId)\n if (!set) {\n set = new Set()\n this.bySession.set(sessionId, set)\n }\n set.add(approvalId)\n this.emit('registered', this.toPublic(entry))\n return this.toPublic(entry)\n }\n\n resolve(\n sessionId: string,\n approvalId: string,\n decision: ApprovalDecision,\n ): boolean {\n const entry = this.pending.get(approvalId)\n if (!entry || entry.sessionId !== sessionId) return false\n if (entry.timer) clearTimeout(entry.timer)\n entry.resolve(decision)\n this.pending.delete(approvalId)\n this.bySession.get(sessionId)?.delete(approvalId)\n return true\n }\n\n cancelSession(sessionId: string): void {\n const ids = this.bySession.get(sessionId)\n if (!ids) return\n for (const id of [...ids]) {\n const entry = this.pending.get(id)\n if (entry) {\n if (entry.timer) clearTimeout(entry.timer)\n entry.resolve({ decision: 'cancel' })\n this.pending.delete(id)\n }\n }\n this.bySession.delete(sessionId)\n }\n\n listPending(sessionId: string): RegisteredApproval[] {\n const ids = this.bySession.get(sessionId)\n if (!ids) return []\n const out: RegisteredApproval[] = []\n for (const id of ids) {\n const entry = this.pending.get(id)\n if (entry) out.push(this.toPublic(entry))\n }\n return out\n }\n\n size(): number {\n return this.pending.size\n }\n\n private toPublic(entry: PendingEntry): RegisteredApproval {\n return {\n approvalId: entry.approvalId,\n agentName: entry.agentName,\n sessionId: entry.sessionId,\n toolCallId: entry.toolCallId,\n toolKind: entry.toolKind,\n toolTitle: entry.toolTitle,\n toolInput: entry.toolInput,\n options: entry.options,\n decisionPromise: entry.decisionPromise,\n }\n }\n}\n","/** Harness-independent delegated-task contracts ([[ZOD072]]). */\nexport interface TaskCallerRef {\n agentName: string\n channelId: string\n threadRoot: string\n sessionKey: string\n}\nexport interface StartTaskSpec {\n agent: string\n prompt: string\n}\nexport interface StartTasksInput {\n tasks: StartTaskSpec[]\n notify?: 'caller' | 'none'\n}\nexport type StartTaskResult =\n | { agent: string; status: 'started'; thread_id: string }\n | {\n agent: string\n status: 'refused' | 'failed'\n reason: string\n attempt_id?: string\n }\nexport interface StartTasksOutput {\n results: StartTaskResult[]\n notify: 'caller' | 'none'\n /** Stated at the point of decision, where the model actually reads it. */\n delivery: string\n}\nexport interface CompleteTaskInput {\n summary: string\n}\nexport interface CompleteTaskOutput {\n status: 'recorded' | 'already_recorded' | 'refused'\n reason?: string\n}\n/** What this session is, so the surface can gate itself instead of refusing later. */\nexport interface TaskRole {\n is_task_assignee: boolean\n can_start_task_threads: boolean\n}\nexport interface TaskActions {\n startTasks(caller: TaskCallerRef, input: StartTasksInput): Promise<StartTasksOutput>\n completeTask(caller: TaskCallerRef, input: CompleteTaskInput): Promise<CompleteTaskOutput>\n describeRole(caller: TaskCallerRef): Promise<TaskRole>\n}\n/** One in-thread handoff inside a delegated task. */\nexport type InvocationState = 'outstanding' | 'returned' | 'cancelled'\nexport interface InvocationRecord {\n invocationId: string\n taskId: string\n callerAgent: string\n callerSessionKey: string\n calleeAgent: string\n callEventId?: string\n calleeSessionKey?: string\n state: InvocationState\n}\n/** Open human-input requests for a session. ZOD078 supplies the implementation. */\nexport interface PendingInputRegistry {\n countFor(sessionKey: string): number\n cancelFor(sessionKeys: string[]): void\n}\nexport const NO_PENDING_INPUT: PendingInputRegistry = {\n countFor: () => 0,\n cancelFor: () => {},\n}\nexport const THREAD_START_FIELD = 'dev.zooid.thread_start'\nexport const THREAD_RESULT_FIELD = 'dev.zooid.thread_result'\nexport interface ThreadStartContent {\n version: 1\n assignee: string\n attempt_id: string\n parent: { agent: string; thread_root: string; session_key: string }\n notify: 'caller' | 'none'\n}\nexport interface ThreadCompletion {\n agent: string\n thread_id: string\n status: 'complete' | 'failed' | 'cancelled' | 'partial'\n output?: { type: 'message'; text: string }\n reason?: string\n error?: string\n}\n"],"mappings":"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","names":["parse","parse","run","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zooid/core",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "zooid core: SessionRunner, Chunker, hooks, config parsing, and the Runtime/Adapter/Transport interfaces.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,9 +24,10 @@
24
24
  "node": ">=22"
25
25
  },
26
26
  "dependencies": {
27
+ "@bufbuild/cel": "^0.6.1",
27
28
  "dotenv-expand": "^13.0.0",
28
29
  "yaml": "^2.5.0",
29
- "@zooid/acp-client": "^0.12.0"
30
+ "@zooid/acp-client": "^0.14.0"
30
31
  },
31
32
  "devDependencies": {
32
33
  "@types/node": "^22.0.0",
@@ -21,6 +21,7 @@ vi.mock('@zooid/acp-client', async (orig) => {
21
21
  ...real,
22
22
  AcpClient: vi.fn().mockImplementation(() => ({
23
23
  start: vi.fn().mockResolvedValue(undefined),
24
+ ensureSession: vi.fn().mockResolvedValue('acp-session-1'),
24
25
  prompt: vi.fn().mockResolvedValue({ stopReason: 'end_turn' }),
25
26
  stop: vi.fn().mockResolvedValue(undefined),
26
27
  })),
@@ -137,6 +138,19 @@ describe('AcpAgentRegistry', () => {
137
138
  expect(opts.runtime).toBe(runtime)
138
139
  })
139
140
 
141
+ it('links every established ACP session, including a resumed one', async () => {
142
+ const linked = vi.fn()
143
+ registry = new AcpAgentRegistry({
144
+ runtime,
145
+ agents: registry.opts.agents,
146
+ onSessionEstablished: linked,
147
+ })
148
+ await registry.ensureSession('triage', 'thread|handoff', '!room:hs')
149
+ await registry.ensureSession('triage', 'thread|handoff', '!room:hs')
150
+ expect(linked).toHaveBeenCalledTimes(2)
151
+ expect(linked).toHaveBeenLastCalledWith('triage', 'thread|handoff', 'acp-session-1')
152
+ })
153
+
140
154
  it('stopAll() invokes stop on every started client', async () => {
141
155
  const { AcpClient } = (await import('@zooid/acp-client')) as unknown as {
142
156
  AcpClient: ReturnType<typeof vi.fn>
@@ -12,15 +12,9 @@ import {
12
12
  } from '@zooid/acp-client'
13
13
  import type { AcpAgentSpec, AcpMount, AcpRuntime } from './acp-types.js'
14
14
  import type { AgentConfig } from './types.js'
15
- import type {
16
- ApprovalCorrelator,
17
- RegisteredApproval,
18
- } from './approval-correlator.js'
15
+ import type { ApprovalCorrelator, RegisteredApproval } from './approval-correlator.js'
19
16
 
20
- export type AcpRegistryEventHandler = (
21
- agentName: string,
22
- event: AgentEvent,
23
- ) => void
17
+ export type AcpRegistryEventHandler = (agentName: string, event: AgentEvent) => void
24
18
  export type AcpRegistryApprovalHandler = (
25
19
  agentName: string,
26
20
  req: ApprovalRequest,
@@ -114,11 +108,18 @@ export interface AcpAgentRegistryOptions {
114
108
  * when the workspace mount is active; falls back to `agent.workdir`.
115
109
  */
116
110
  cwd?: Record<string, string>
111
+ /**
112
+ * Called after an ACP session is created or recovered. Context adapters
113
+ * which cannot receive an MCP spawn id (Pi) use this to resolve their
114
+ * daemon-side binding by the ACP session id instead.
115
+ */
116
+ onSessionEstablished?: (agentName: string, sessionKey: string, sessionId: string) => void
117
117
  }
118
118
 
119
119
  export type ContextSpawnFactory = (
120
120
  threadId: string,
121
121
  channelId?: string,
122
+ sessionKey?: string,
122
123
  ) => Promise<{
123
124
  name: 'zooid-context'
124
125
  command: string
@@ -174,11 +175,7 @@ export class AcpAgentRegistry implements AcpRegistry {
174
175
  }
175
176
 
176
177
  resolveSpawnCwd(name: string): string {
177
- return (
178
- this.opts.cwd?.[name] ??
179
- this.opts.agents[name]?.workdir ??
180
- process.cwd()
181
- )
178
+ return this.opts.cwd?.[name] ?? this.opts.agents[name]?.workdir ?? process.cwd()
182
179
  }
183
180
 
184
181
  agentNames(): string[] {
@@ -197,7 +194,9 @@ export class AcpAgentRegistry implements AcpRegistry {
197
194
  ): Promise<string> {
198
195
  if (!this.hasAgent(name)) throw new Error(`unknown agent: ${name}`)
199
196
  const client = await this.ensureClient(name)
200
- return client.ensureSession(threadId, channelId, contextThreadId)
197
+ const sessionId = await client.ensureSession(threadId, channelId, contextThreadId)
198
+ this.opts.onSessionEstablished?.(name, threadId, sessionId)
199
+ return sessionId
201
200
  }
202
201
 
203
202
  endSession(name: string, threadId: string): void {
@@ -229,9 +228,7 @@ export class AcpAgentRegistry implements AcpRegistry {
229
228
  }
230
229
 
231
230
  async stopAll(): Promise<void> {
232
- await Promise.allSettled(
233
- [...this.clients.values()].map((c) => c.stop()),
234
- )
231
+ await Promise.allSettled([...this.clients.values()].map((c) => c.stop()))
235
232
  this.clients.clear()
236
233
  }
237
234
 
package/src/acp-types.ts CHANGED
@@ -4,7 +4,8 @@ import type { ChildProcess } from 'node:child_process'
4
4
  * Per-agent ACP block in zooid.yaml. XOR: either a known preset or an
5
5
  * explicit command. The schema parser rejects both/neither.
6
6
  *
7
- * Built-in presets: `claude`, `codex`, `opencode`, `cline`, `kiro`, `gemini`.
7
+ * Built-in presets: `claude`, `codex`, `opencode`, `pi`, `cline`, `kiro`,
8
+ * `gemini`.
8
9
  * See `@zooid/acp-client`'s preset registry for the current list.
9
10
  */
10
11
  export type AcpAgentSpec =
@@ -77,6 +77,7 @@ ${HTTP_TRANSPORT.trimStart()}${QA_AGENTS}`)
77
77
  },
78
78
  },
79
79
  hooks: {},
80
+ triggers: {},
80
81
  })
81
82
  })
82
83