@zooid/core 0.7.1 → 0.7.3
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.d.ts +32 -6
- package/dist/index.js +35 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/config.test.ts +88 -9
- package/src/config.ts +38 -11
- package/src/index.ts +1 -0
- package/src/types.ts +32 -5
- package/src/zooid-yaml-sweep.test.ts +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -124,6 +124,27 @@ interface ContainerConfig {
|
|
|
124
124
|
interface ZooidContainerConfig {
|
|
125
125
|
image?: string;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* A room binding for an agent. Either a bare alias (default PL) or
|
|
129
|
+
* an alias with a declared power level applied at room creation.
|
|
130
|
+
*
|
|
131
|
+
* `alias` may be an alias (`#room:server`) or a room ID (`!id:server`).
|
|
132
|
+
* Resolved to a canonical room ID by `bot-pool` at bootstrap. The
|
|
133
|
+
* declared `powerLevel` (when set) seeds the agent's entry in
|
|
134
|
+
* `m.room.power_levels.users` at room creation; the daemon never reads
|
|
135
|
+
* or modifies power levels after that — promote/demote in the UI is
|
|
136
|
+
* canonical.
|
|
137
|
+
*/
|
|
138
|
+
interface RoomBinding {
|
|
139
|
+
/** Room alias (typical) or room ID. */
|
|
140
|
+
alias: string;
|
|
141
|
+
/**
|
|
142
|
+
* Power level the agent should hold in this room at the moment it is
|
|
143
|
+
* created. Omitted = `users_default` (effectively 0). Not reconciled
|
|
144
|
+
* after creation.
|
|
145
|
+
*/
|
|
146
|
+
powerLevel?: number;
|
|
147
|
+
}
|
|
127
148
|
/**
|
|
128
149
|
* Matrix transport binding. Lives under `agents.<name>.matrix:` in
|
|
129
150
|
* zooid.yaml. The block name (`matrix`) is the transport-kind
|
|
@@ -146,8 +167,8 @@ interface MatrixBinding {
|
|
|
146
167
|
* profile on bootstrap. Falls back to the user_id localpart when absent.
|
|
147
168
|
*/
|
|
148
169
|
display_name?: string;
|
|
149
|
-
/**
|
|
150
|
-
rooms:
|
|
170
|
+
/** Rooms this agent watches. Each entry carries the alias/ID and an optional declared PL. */
|
|
171
|
+
rooms: RoomBinding[];
|
|
151
172
|
/**
|
|
152
173
|
* Routing rule. `mention` requires the bot to be tagged; `any` triggers
|
|
153
174
|
* on every message.
|
|
@@ -204,7 +225,9 @@ interface AgentConfig {
|
|
|
204
225
|
}
|
|
205
226
|
/**
|
|
206
227
|
* Matrix application-service transport. The CLI binds the AS HTTP listener
|
|
207
|
-
* to `port` (defaults to
|
|
228
|
+
* to `port` (defaults to 9000 — the most common Matrix AS convention; see
|
|
229
|
+
* Synapse / mautrix / matrix-appservice-* projects). Must match the port in
|
|
230
|
+
* the registration YAML's `url` (read by the homeserver, not by Zooid).
|
|
208
231
|
*/
|
|
209
232
|
interface MatrixTransportConfig {
|
|
210
233
|
type: 'matrix';
|
|
@@ -230,8 +253,11 @@ interface MatrixTransportConfig {
|
|
|
230
253
|
*/
|
|
231
254
|
user_namespace: string;
|
|
232
255
|
/**
|
|
233
|
-
* AS HTTP listener port.
|
|
234
|
-
*
|
|
256
|
+
* AS HTTP listener port. Must match the registration YAML's `url` port —
|
|
257
|
+
* Zooid never reads the registration, so a mismatch silently sinks every
|
|
258
|
+
* transaction (the homeserver gets connection refused; you see no error
|
|
259
|
+
* in Zooid's logs).
|
|
260
|
+
* @default 9000
|
|
235
261
|
*/
|
|
236
262
|
port?: number;
|
|
237
263
|
/**
|
|
@@ -538,4 +564,4 @@ interface TransportContextProvider {
|
|
|
538
564
|
getChannelInfo(channelId: string): Promise<ChannelInfo>;
|
|
539
565
|
}
|
|
540
566
|
|
|
541
|
-
export { AcpAgentRegistry, type AcpAgentRegistryOptions, type AcpAgentSpec, type AcpMount, type AcpRegistry, type AcpRegistryApprovalHandler, type AcpRegistryEventHandler, type AcpRuntime, type AcpSpawnSpec, type AgentConfig, ApprovalCorrelator, type ChannelInfo, type CliFlags, type ContainerConfig, type ContextSpawnFactory, type HistoryOptions, type HistoryPage, type HttpBinding, type HttpTransportConfig, type InboundMessage, type LoadZooidConfigOptions, type MatrixBinding, type MatrixTransportConfig, type Member, type Message, type MountConfig, type RegisterOptions, type RegisteredApproval, type ThreadOverview, type ThreadOverviewPage, type ThreadRef, type Transport, type TransportConfig, type TransportContextProvider, type ZooidConfig, type ZooidContainerConfig, findConfigFile, findHttpTransport, findMatrixTransport, findTransport, loadZooidConfig, mergeCliFlags, resolveAcpAgentSpec };
|
|
567
|
+
export { AcpAgentRegistry, type AcpAgentRegistryOptions, type AcpAgentSpec, type AcpMount, type AcpRegistry, type AcpRegistryApprovalHandler, type AcpRegistryEventHandler, type AcpRuntime, type AcpSpawnSpec, type AgentConfig, ApprovalCorrelator, type ChannelInfo, type CliFlags, type ContainerConfig, type ContextSpawnFactory, type HistoryOptions, type HistoryPage, type HttpBinding, type HttpTransportConfig, type InboundMessage, type LoadZooidConfigOptions, type MatrixBinding, type MatrixTransportConfig, type Member, type Message, type MountConfig, type RegisterOptions, type RegisteredApproval, type RoomBinding, type ThreadOverview, type ThreadOverviewPage, type ThreadRef, type Transport, type TransportConfig, type TransportContextProvider, type ZooidConfig, type ZooidContainerConfig, findConfigFile, findHttpTransport, findMatrixTransport, findTransport, loadZooidConfig, mergeCliFlags, resolveAcpAgentSpec };
|
package/dist/index.js
CHANGED
|
@@ -432,6 +432,39 @@ function parseTransport(name, raw, processEnv) {
|
|
|
432
432
|
}
|
|
433
433
|
return { type: "http", port };
|
|
434
434
|
}
|
|
435
|
+
function parseRoomBinding(path, raw, serverName) {
|
|
436
|
+
function normalizeAlias(alias) {
|
|
437
|
+
if (alias.length === 0) {
|
|
438
|
+
throw new Error(`${path}: must be a non-empty alias`);
|
|
439
|
+
}
|
|
440
|
+
if (!MATRIX_ROOM_IDENT_RE.test(alias)) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
`${path}: must start with '#' or '!' (got ${JSON.stringify(alias)})`
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
return alias.includes(":") ? alias : `${alias}:${serverName}`;
|
|
446
|
+
}
|
|
447
|
+
if (typeof raw === "string") {
|
|
448
|
+
return { alias: normalizeAlias(raw) };
|
|
449
|
+
}
|
|
450
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
451
|
+
throw new Error(`${path}: must be a string or { alias, power_level } object`);
|
|
452
|
+
}
|
|
453
|
+
const r = raw;
|
|
454
|
+
if (typeof r.alias !== "string" || r.alias.length === 0) {
|
|
455
|
+
throw new Error(`${path}.alias: must be a non-empty string`);
|
|
456
|
+
}
|
|
457
|
+
const out = { alias: normalizeAlias(r.alias) };
|
|
458
|
+
if (r.power_level !== void 0) {
|
|
459
|
+
if (typeof r.power_level !== "number" || !Number.isInteger(r.power_level)) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
`${path}.power_level: must be an integer (got ${JSON.stringify(r.power_level)})`
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
out.powerLevel = r.power_level;
|
|
465
|
+
}
|
|
466
|
+
return out;
|
|
467
|
+
}
|
|
435
468
|
function parseTransportBinding(name, entry, transports) {
|
|
436
469
|
const present = TRANSPORT_KINDS.filter(
|
|
437
470
|
(k) => entry[k] !== void 0 && entry[k] !== null
|
|
@@ -501,16 +534,8 @@ function parseTransportBinding(name, entry, transports) {
|
|
|
501
534
|
throw new Error(`agents.${name}.matrix.rooms is required and must be a non-empty array`);
|
|
502
535
|
}
|
|
503
536
|
const rooms = [];
|
|
504
|
-
for (
|
|
505
|
-
|
|
506
|
-
throw new Error(`agents.${name}.matrix.rooms[] must be a non-empty string`);
|
|
507
|
-
}
|
|
508
|
-
if (!MATRIX_ROOM_IDENT_RE.test(r)) {
|
|
509
|
-
throw new Error(
|
|
510
|
-
`agents.${name}.matrix.rooms[] must start with '#' or '!' (got ${JSON.stringify(r)})`
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
rooms.push(r.includes(":") ? r : `${r}:${serverName}`);
|
|
537
|
+
for (let i = 0; i < block.rooms.length; i++) {
|
|
538
|
+
rooms.push(parseRoomBinding(`agents.${name}.matrix.rooms[${i}]`, block.rooms[i], serverName));
|
|
514
539
|
}
|
|
515
540
|
let displayName;
|
|
516
541
|
if (block.display_name !== void 0) {
|
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 TransportConfig,\n ZooidConfig,\n ZooidContainerConfig,\n} from './types.js'\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): 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)\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): 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 if (r.sender_localpart === undefined) r.sender_localpart = 'zooid'\n if (r.user_namespace === undefined && typeof r.homeserver === 'string') {\n try {\n const host = new URL(\n interpolateString(r.homeserver as string, processEnv),\n ).hostname\n if (host) r.user_namespace = `@.*:${host}`\n } catch {\n // Fall through: the required-fields loop will fire its \"must be a\n // non-empty string\" error, which is the same message today's parser\n // would emit for a bad homeserver URL.\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 }\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 }\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 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 const rawUserId =\n typeof block.user_id === 'string' && block.user_id.length > 0\n ? block.user_id\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: string[] = []\n for (const r of block.rooms) {\n if (typeof r !== 'string' || r.length === 0) {\n throw new Error(`agents.${name}.matrix.rooms[] must be a non-empty string`)\n }\n if (!MATRIX_ROOM_IDENT_RE.test(r)) {\n throw new Error(\n `agents.${name}.matrix.rooms[] must start with '#' or '!' (got ${JSON.stringify(r)})`,\n )\n }\n rooms.push(r.includes(':') ? r : `${r}:${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 const transports = parseTransports(r.transports, processEnv)\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 (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(name: string, threadId: string, channelId?: string): 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(name: string, threadId: string, channelId?: string): 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)\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/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\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): 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)\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): 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 if (r.sender_localpart === undefined) r.sender_localpart = 'zooid'\n if (r.user_namespace === undefined && typeof r.homeserver === 'string') {\n try {\n const host = new URL(\n interpolateString(r.homeserver as string, processEnv),\n ).hostname\n if (host) r.user_namespace = `@.*:${host}`\n } catch {\n // Fall through: the required-fields loop will fire its \"must be a\n // non-empty string\" error, which is the same message today's parser\n // would emit for a bad homeserver URL.\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 }\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 }\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 const rawUserId =\n typeof block.user_id === 'string' && block.user_id.length > 0\n ? block.user_id\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 const transports = parseTransports(r.transports, processEnv)\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 (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(name: string, threadId: string, channelId?: string): 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(name: string, threadId: string, channelId?: string): 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)\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zooid/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "zooid core: SessionRunner, Chunker, hooks, config parsing, and the Runtime/Adapter/Transport interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"dotenv-expand": "^13.0.0",
|
|
28
28
|
"yaml": "^2.5.0",
|
|
29
|
-
"@zooid/acp-client": "^0.7.
|
|
29
|
+
"@zooid/acp-client": "^0.7.3"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.0.0",
|
package/src/config.test.ts
CHANGED
|
@@ -401,7 +401,7 @@ agents:
|
|
|
401
401
|
expect(t.user_namespace).toBe('@.*:localhost')
|
|
402
402
|
}
|
|
403
403
|
expect(config.agents.architect!.matrix?.user_id).toBe('@architect:localhost')
|
|
404
|
-
expect(config.agents.architect!.matrix?.rooms).toEqual(['!r1:localhost'])
|
|
404
|
+
expect(config.agents.architect!.matrix?.rooms).toEqual([{ alias: '!r1:localhost' }])
|
|
405
405
|
expect(config.agents.architect!.matrix?.trigger).toBe('mention')
|
|
406
406
|
})
|
|
407
407
|
|
|
@@ -649,8 +649,8 @@ agents:
|
|
|
649
649
|
- '#docs'
|
|
650
650
|
`)
|
|
651
651
|
expect(config.agents.docs!.matrix?.rooms).toEqual([
|
|
652
|
-
'#welcome:localhost',
|
|
653
|
-
'#docs:localhost',
|
|
652
|
+
{ alias: '#welcome:localhost' },
|
|
653
|
+
{ alias: '#docs:localhost' },
|
|
654
654
|
])
|
|
655
655
|
})
|
|
656
656
|
|
|
@@ -668,7 +668,7 @@ agents:
|
|
|
668
668
|
rooms:
|
|
669
669
|
- '!abc'
|
|
670
670
|
`)
|
|
671
|
-
expect(config.agents.docs!.matrix?.rooms).toEqual(['!abc:localhost'])
|
|
671
|
+
expect(config.agents.docs!.matrix?.rooms).toEqual([{ alias: '!abc:localhost' }])
|
|
672
672
|
})
|
|
673
673
|
|
|
674
674
|
it('leaves fully-qualified user_id and rooms untouched (mixed forms in one binding)', () => {
|
|
@@ -689,9 +689,9 @@ agents:
|
|
|
689
689
|
`)
|
|
690
690
|
expect(config.agents.docs!.matrix?.user_id).toBe('@docs:localhost')
|
|
691
691
|
expect(config.agents.docs!.matrix?.rooms).toEqual([
|
|
692
|
-
'#welcome:localhost',
|
|
693
|
-
'#docs:localhost',
|
|
694
|
-
'!r1:localhost',
|
|
692
|
+
{ alias: '#welcome:localhost' },
|
|
693
|
+
{ alias: '#docs:localhost' },
|
|
694
|
+
{ alias: '!r1:localhost' },
|
|
695
695
|
])
|
|
696
696
|
})
|
|
697
697
|
|
|
@@ -728,7 +728,86 @@ agents:
|
|
|
728
728
|
rooms:
|
|
729
729
|
- 'welcome'
|
|
730
730
|
`),
|
|
731
|
-
).toThrow(/
|
|
731
|
+
).toThrow(/must start with '#' or '!'/)
|
|
732
|
+
})
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
describe('agent matrix.rooms power_level binding', () => {
|
|
736
|
+
it('accepts bare-alias strings (default PL, no powerLevel field)', () => {
|
|
737
|
+
const cfg = loadZooidConfig(`
|
|
738
|
+
runtime: local
|
|
739
|
+
${MATRIX_TRANSPORT.trimStart()}
|
|
740
|
+
agents:
|
|
741
|
+
bot:
|
|
742
|
+
workdir: ./bot
|
|
743
|
+
acp: { preset: claude }
|
|
744
|
+
matrix:
|
|
745
|
+
transport: matrix-local
|
|
746
|
+
user_id: '@bot:localhost'
|
|
747
|
+
rooms: ['#general']
|
|
748
|
+
trigger: mention
|
|
749
|
+
`)
|
|
750
|
+
expect(cfg.agents.bot!.matrix?.rooms).toEqual([{ alias: '#general:localhost' }])
|
|
751
|
+
})
|
|
752
|
+
|
|
753
|
+
it('accepts { alias, power_level } object entries and normalizes them', () => {
|
|
754
|
+
const cfg = loadZooidConfig(`
|
|
755
|
+
runtime: local
|
|
756
|
+
${MATRIX_TRANSPORT.trimStart()}
|
|
757
|
+
agents:
|
|
758
|
+
mod:
|
|
759
|
+
workdir: ./mod
|
|
760
|
+
acp: { preset: claude }
|
|
761
|
+
matrix:
|
|
762
|
+
transport: matrix-local
|
|
763
|
+
user_id: '@mod:localhost'
|
|
764
|
+
rooms:
|
|
765
|
+
- '#general'
|
|
766
|
+
- { alias: '#chat', power_level: 50 }
|
|
767
|
+
trigger: mention
|
|
768
|
+
`)
|
|
769
|
+
expect(cfg.agents.mod!.matrix?.rooms).toEqual([
|
|
770
|
+
{ alias: '#general:localhost' },
|
|
771
|
+
{ alias: '#chat:localhost', powerLevel: 50 },
|
|
772
|
+
])
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
it('rejects an object entry without a string alias', () => {
|
|
776
|
+
expect(() =>
|
|
777
|
+
loadZooidConfig(`
|
|
778
|
+
runtime: local
|
|
779
|
+
${MATRIX_TRANSPORT.trimStart()}
|
|
780
|
+
agents:
|
|
781
|
+
mod:
|
|
782
|
+
workdir: ./mod
|
|
783
|
+
acp: { preset: claude }
|
|
784
|
+
matrix:
|
|
785
|
+
transport: matrix-local
|
|
786
|
+
user_id: '@mod:localhost'
|
|
787
|
+
rooms:
|
|
788
|
+
- { power_level: 50 }
|
|
789
|
+
trigger: mention
|
|
790
|
+
`),
|
|
791
|
+
).toThrow(/alias/)
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
it('rejects a non-integer power_level', () => {
|
|
795
|
+
expect(() =>
|
|
796
|
+
loadZooidConfig(`
|
|
797
|
+
runtime: local
|
|
798
|
+
${MATRIX_TRANSPORT.trimStart()}
|
|
799
|
+
agents:
|
|
800
|
+
mod:
|
|
801
|
+
workdir: ./mod
|
|
802
|
+
acp: { preset: claude }
|
|
803
|
+
matrix:
|
|
804
|
+
transport: matrix-local
|
|
805
|
+
user_id: '@mod:localhost'
|
|
806
|
+
rooms:
|
|
807
|
+
- { alias: '#chat', power_level: 'high' }
|
|
808
|
+
trigger: mention
|
|
809
|
+
`),
|
|
810
|
+
).toThrow(/power_level/)
|
|
732
811
|
})
|
|
733
812
|
})
|
|
734
813
|
|
|
@@ -1280,7 +1359,7 @@ agents:
|
|
|
1280
1359
|
expect(cfg.agents.echo.workdir).toBe('./agents/echo')
|
|
1281
1360
|
expect(cfg.agents.echo.matrix?.transport).toBe('matrix')
|
|
1282
1361
|
expect(cfg.agents.echo.matrix?.user_id).toBe('@echo:localhost')
|
|
1283
|
-
expect(cfg.agents.echo.matrix?.rooms).toEqual(['#welcome:localhost'])
|
|
1362
|
+
expect(cfg.agents.echo.matrix?.rooms).toEqual([{ alias: '#welcome:localhost' }])
|
|
1284
1363
|
|
|
1285
1364
|
expect(cfg.agents.docs.workdir).toBe('./agents/docs')
|
|
1286
1365
|
expect(cfg.agents.docs.matrix?.user_id).toBe('@docs:localhost')
|
package/src/config.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
MatrixBinding,
|
|
14
14
|
MatrixTransportConfig,
|
|
15
15
|
MountConfig,
|
|
16
|
+
RoomBinding,
|
|
16
17
|
TransportConfig,
|
|
17
18
|
ZooidConfig,
|
|
18
19
|
ZooidContainerConfig,
|
|
@@ -452,6 +453,40 @@ function parseTransport(
|
|
|
452
453
|
return { type: 'http', port }
|
|
453
454
|
}
|
|
454
455
|
|
|
456
|
+
function parseRoomBinding(path: string, raw: unknown, serverName: string): RoomBinding {
|
|
457
|
+
function normalizeAlias(alias: string): string {
|
|
458
|
+
if (alias.length === 0) {
|
|
459
|
+
throw new Error(`${path}: must be a non-empty alias`)
|
|
460
|
+
}
|
|
461
|
+
if (!MATRIX_ROOM_IDENT_RE.test(alias)) {
|
|
462
|
+
throw new Error(
|
|
463
|
+
`${path}: must start with '#' or '!' (got ${JSON.stringify(alias)})`,
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
return alias.includes(':') ? alias : `${alias}:${serverName}`
|
|
467
|
+
}
|
|
468
|
+
if (typeof raw === 'string') {
|
|
469
|
+
return { alias: normalizeAlias(raw) }
|
|
470
|
+
}
|
|
471
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
472
|
+
throw new Error(`${path}: must be a string or { alias, power_level } object`)
|
|
473
|
+
}
|
|
474
|
+
const r = raw as Record<string, unknown>
|
|
475
|
+
if (typeof r.alias !== 'string' || r.alias.length === 0) {
|
|
476
|
+
throw new Error(`${path}.alias: must be a non-empty string`)
|
|
477
|
+
}
|
|
478
|
+
const out: RoomBinding = { alias: normalizeAlias(r.alias) }
|
|
479
|
+
if (r.power_level !== undefined) {
|
|
480
|
+
if (typeof r.power_level !== 'number' || !Number.isInteger(r.power_level)) {
|
|
481
|
+
throw new Error(
|
|
482
|
+
`${path}.power_level: must be an integer (got ${JSON.stringify(r.power_level)})`,
|
|
483
|
+
)
|
|
484
|
+
}
|
|
485
|
+
out.powerLevel = r.power_level
|
|
486
|
+
}
|
|
487
|
+
return out
|
|
488
|
+
}
|
|
489
|
+
|
|
455
490
|
function parseTransportBinding(
|
|
456
491
|
name: string,
|
|
457
492
|
entry: Record<string, unknown>,
|
|
@@ -535,17 +570,9 @@ function parseTransportBinding(
|
|
|
535
570
|
if (!Array.isArray(block.rooms) || block.rooms.length === 0) {
|
|
536
571
|
throw new Error(`agents.${name}.matrix.rooms is required and must be a non-empty array`)
|
|
537
572
|
}
|
|
538
|
-
const rooms:
|
|
539
|
-
for (
|
|
540
|
-
|
|
541
|
-
throw new Error(`agents.${name}.matrix.rooms[] must be a non-empty string`)
|
|
542
|
-
}
|
|
543
|
-
if (!MATRIX_ROOM_IDENT_RE.test(r)) {
|
|
544
|
-
throw new Error(
|
|
545
|
-
`agents.${name}.matrix.rooms[] must start with '#' or '!' (got ${JSON.stringify(r)})`,
|
|
546
|
-
)
|
|
547
|
-
}
|
|
548
|
-
rooms.push(r.includes(':') ? r : `${r}:${serverName}`)
|
|
573
|
+
const rooms: RoomBinding[] = []
|
|
574
|
+
for (let i = 0; i < block.rooms.length; i++) {
|
|
575
|
+
rooms.push(parseRoomBinding(`agents.${name}.matrix.rooms[${i}]`, block.rooms[i], serverName))
|
|
549
576
|
}
|
|
550
577
|
|
|
551
578
|
let displayName: string | undefined
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -76,6 +76,28 @@ export interface ZooidContainerConfig {
|
|
|
76
76
|
image?: string
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* A room binding for an agent. Either a bare alias (default PL) or
|
|
81
|
+
* an alias with a declared power level applied at room creation.
|
|
82
|
+
*
|
|
83
|
+
* `alias` may be an alias (`#room:server`) or a room ID (`!id:server`).
|
|
84
|
+
* Resolved to a canonical room ID by `bot-pool` at bootstrap. The
|
|
85
|
+
* declared `powerLevel` (when set) seeds the agent's entry in
|
|
86
|
+
* `m.room.power_levels.users` at room creation; the daemon never reads
|
|
87
|
+
* or modifies power levels after that — promote/demote in the UI is
|
|
88
|
+
* canonical.
|
|
89
|
+
*/
|
|
90
|
+
export interface RoomBinding {
|
|
91
|
+
/** Room alias (typical) or room ID. */
|
|
92
|
+
alias: string
|
|
93
|
+
/**
|
|
94
|
+
* Power level the agent should hold in this room at the moment it is
|
|
95
|
+
* created. Omitted = `users_default` (effectively 0). Not reconciled
|
|
96
|
+
* after creation.
|
|
97
|
+
*/
|
|
98
|
+
powerLevel?: number
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
/**
|
|
80
102
|
* Matrix transport binding. Lives under `agents.<name>.matrix:` in
|
|
81
103
|
* zooid.yaml. The block name (`matrix`) is the transport-kind
|
|
@@ -98,8 +120,8 @@ export interface MatrixBinding {
|
|
|
98
120
|
* profile on bootstrap. Falls back to the user_id localpart when absent.
|
|
99
121
|
*/
|
|
100
122
|
display_name?: string
|
|
101
|
-
/**
|
|
102
|
-
rooms:
|
|
123
|
+
/** Rooms this agent watches. Each entry carries the alias/ID and an optional declared PL. */
|
|
124
|
+
rooms: RoomBinding[]
|
|
103
125
|
/**
|
|
104
126
|
* Routing rule. `mention` requires the bot to be tagged; `any` triggers
|
|
105
127
|
* on every message.
|
|
@@ -159,7 +181,9 @@ export interface AgentConfig {
|
|
|
159
181
|
|
|
160
182
|
/**
|
|
161
183
|
* Matrix application-service transport. The CLI binds the AS HTTP listener
|
|
162
|
-
* to `port` (defaults to
|
|
184
|
+
* to `port` (defaults to 9000 — the most common Matrix AS convention; see
|
|
185
|
+
* Synapse / mautrix / matrix-appservice-* projects). Must match the port in
|
|
186
|
+
* the registration YAML's `url` (read by the homeserver, not by Zooid).
|
|
163
187
|
*/
|
|
164
188
|
export interface MatrixTransportConfig {
|
|
165
189
|
type: 'matrix'
|
|
@@ -185,8 +209,11 @@ export interface MatrixTransportConfig {
|
|
|
185
209
|
*/
|
|
186
210
|
user_namespace: string
|
|
187
211
|
/**
|
|
188
|
-
* AS HTTP listener port.
|
|
189
|
-
*
|
|
212
|
+
* AS HTTP listener port. Must match the registration YAML's `url` port —
|
|
213
|
+
* Zooid never reads the registration, so a mismatch silently sinks every
|
|
214
|
+
* transaction (the homeserver gets connection refused; you see no error
|
|
215
|
+
* in Zooid's logs).
|
|
216
|
+
* @default 9000
|
|
190
217
|
*/
|
|
191
218
|
port?: number
|
|
192
219
|
/**
|
|
@@ -215,7 +215,7 @@ agents:
|
|
|
215
215
|
expect(cfg.agents.alice!.matrix).toEqual({
|
|
216
216
|
transport: 'm1',
|
|
217
217
|
user_id: '@alice:localhost',
|
|
218
|
-
rooms: ['!r:localhost'],
|
|
218
|
+
rooms: [{ alias: '!r:localhost' }],
|
|
219
219
|
trigger: 'any',
|
|
220
220
|
})
|
|
221
221
|
expect(cfg.agents.alice!.http).toBeUndefined()
|