/** * Registry-backed agent resolver with 5-tier precedence. * * Lookup order (highest wins): * 1. `project` — rows in global `agent_registry_agents` tagged * `tier='project'` (attached from * `/.cleo/cant/agents/`). * 2. `global` — rows tagged `tier='global'` installed from * `~/.local/share/cleo/cant/agents/`. * 3. `packaged` — rows tagged `tier='packaged'` installed from the * bundled `@cleocode/agents/templates/` tree. * 4. `fallback` — no row exists; a synthetic `ResolvedAgent` is * synthesized on-the-fly from the bundled * `templates/.cant` file if one is on disk. * After ADR-068 D1+D2, filenames match declared names * exactly (e.g. `project-docs-worker.cant`), so the * classifier's `project-` output resolves directly. * 5. `universal` — tiers 1-4 all missed; a synthetic `ResolvedAgent` * is synthesized from the universal protocol base at * `@cleocode/agents/cleo-subagent.cant`. Added in * v2026.4.111 (T1241 / D035) so classifier output can * never trigger `E_AGENT_NOT_FOUND` when the universal * base file is reachable. Emits a WARN log when taken. * Wired into the spawn validator pre-flight by T1933 * (ADR-068 Decision 6 — supersedes ADR-055 D035). * * The GLOBAL `agent_registry_agents` row is the single source of truth for * tier-aware metadata. The `agent_registry_agents.agent_id` column is UNIQUE across the * whole table (see `agent-registry-store.ts` schema), so a single row exists * per agent — `tier` records which directory holds the canonical `.cant`. * * Orphan-row detection (doctor D-002): if the row's `cant_path` points at * a file that no longer exists, the row is skipped and resolution falls * through to the next tier instead of failing hard. This preserves spawn * availability when operators delete a tier's `.cant` directory without * running `cleo agent doctor`. * * Registry DB is always the SSoT for metadata; filesystem is secondary. * * @module agent-resolver * @task T889 / T898 / T899 / W2-4 / T1241 * @epic T889 / T1232 */ import type { DatabaseSync as _DatabaseSyncType } from 'node:sqlite'; import type { AgentTier, ResolvedAgent } from '@cleocode/contracts'; type DatabaseSync = _DatabaseSyncType; /** * Canonical tier identifier for the universal-base fallback (5th tier). * * Exported so dispatch-layer code can compare against a single source of * truth rather than string-literal the value in multiple places. * * @task T1241 / D035 */ export declare const AGENT_TIER_UNIVERSAL: AgentTier; /** * Canonical agentId of the universal protocol base shipped by * `@cleocode/agents`. Used exclusively by the 5th-tier universal fallback * path in {@link resolveAgent}. * * @task T1241 / D035 */ export declare const AGENT_UNIVERSAL_BASE_ID = "cleo-subagent"; /** * Deprecated agent IDs that should be rewritten to their replacement. * * Reserved as an extension point for future clean-forward migrations. Empty * by default — CLEO does not ship backward-compatibility aliases for agent * IDs. Agent renames must be propagated to every call site before release. * * When an alias is applied, `ResolvedAgent.aliasApplied` is set to `true` * and `aliasTarget` is populated with the effective canonical id so callers * can emit a deprecation warning at the UI layer. * * @task T889 / W2-4 · T1257 (emptied per clean-forward directive) */ export declare const DEPRECATED_ALIASES: Readonly>; /** * Thrown when every tier in `resolveAgent` fails to produce a record. * * Exit code `65` is reserved for agent-resolution misses by the CLI dispatch * layer. The attached `triedTiers` list records the lookup order actually * walked so operators can see which tiers were exhausted. * * @task T889 / W2-4 */ export declare class AgentNotFoundError extends Error { readonly agentId: string; readonly triedTiers: AgentTier[]; readonly code = "E_AGENT_NOT_FOUND"; readonly exitCode = 65; constructor(agentId: string, triedTiers: AgentTier[]); } /** * Optional configuration for {@link resolveAgent}. * * @task T889 / W2-4 */ export interface ResolveAgentOptions { /** * Absolute path to the project root. Present in the envelope for callers * that want to display where the resolved project-tier `.cant` lives, but * the resolver does NOT perform its own filesystem walk inside the project * — the global cleo.db (Agent Registry) is consulted regardless of tier. */ projectRoot?: string; /** * Preferred tier to try first. When supplied, the tier is moved to the * head of the lookup order; the remaining tiers follow in the default * sequence `project → global → packaged → fallback`. */ preferTier?: AgentTier; /** * When `true`, skip the {@link DEPRECATED_ALIASES} remap. Reserved for the * doctor walk, which needs to inspect the literal id on disk. */ skipAliasCheck?: boolean; /** * Absolute path to the bundled `templates/` directory used by the * `fallback` tier. When unset the resolver derives a default that climbs * out of `packages/core/dist` into `packages/agents/templates/`. Tests * can pin this to an isolated fixture directory. * * Replaces the former `seed-agents/` path per ADR-068 Decision 1 + 2. * The field name is preserved for backward compatibility with existing * test call sites. */ packagedSeedDir?: string; /** * Absolute path to the universal protocol base `.cant` file used by the * `universal` (5th) tier. When unset the resolver derives a default that * climbs out of `packages/core/dist` into * `packages/agents/cleo-subagent.cant`. Tests can pin this to an * isolated fixture file; passing a path that does not exist is equivalent * to disabling the tier and causes the resolver to throw * {@link AgentNotFoundError} when all four prior tiers miss. * * @task T1241 / D035 */ universalBasePath?: string; } /** * Resolve a single agent using the 5-tier registry precedence. * * Tiers walked in order: `project` → `global` → `packaged` → `fallback` → * `universal`. The 5th (`universal`) tier was added in v2026.4.111 (T1241) * and synthesises an envelope from `@cleocode/agents/cleo-subagent.cant` * when every prior tier misses. As long as the universal-base file is * reachable on disk, this function no longer throws * {@link AgentNotFoundError} — the error becomes genuinely exceptional * (catastrophic missing base file) rather than a routine resolution miss. * * @param db - Open handle to global cleo.db (Agent Registry). Caller owns lifecycle. * @param agentId - Business identifier of the agent to resolve. * @param options - Optional lookup overrides (see {@link ResolveAgentOptions}). * @returns The highest-precedence {@link ResolvedAgent} envelope. * @throws {AgentNotFoundError} When every tier — including the universal * base fallback — misses. Surfaces only when * `@cleocode/agents/cleo-subagent.cant` is * unreachable. * @task T889 / W2-4 / T1241 */ export declare function resolveAgent(db: DatabaseSync, agentId: string, options?: ResolveAgentOptions): ResolvedAgent; /** * Resolve a batch of agent ids, collecting misses as * {@link AgentNotFoundError} entries in the result map instead of throwing. * * Any unexpected error from {@link resolveAgent} propagates. Only the * structured "not found" envelope is collected as a per-id value. * * @param db - Open handle to global cleo.db (Agent Registry). * @param agentIds - Business ids to resolve. * @param options - Shared lookup options applied to every id. * @returns Map keyed by agentId with the envelope or the not-found error. * @task T889 / W2-4 */ export declare function resolveAgentsBatch(db: DatabaseSync, agentIds: string[], options?: ResolveAgentOptions): Map; /** * Read the skill slugs attached to an agent via the `agent_skills` junction. * * The junction is the source of truth for skill bindings (see * `agent-registry-accessor.ts`). Skills JSON on `agents.skills` is a * materialised cache — this helper reads junction rows directly so callers * always see post-install state. * * Lookup: `agents.agent_id = ?` → `agents.id` → junction `agent_skills` → * `skills.slug`. Ordered by `attached_at DESC` (most-recent attachment first). * * @param db - Open handle to global cleo.db (Agent Registry). * @param agentId - Business id of the agent whose skills to read. * @returns Skill slugs, deduplicated. Returns `[]` when none attached. * @task T889 / W2-4 */ export declare function getAgentSkills(db: DatabaseSync, agentId: string): string[]; /** * Compute the default directory used by the `fallback` tier. * * Climbs out of the compiled `packages/core/dist/store/` location and into * the sibling `packages/agents/templates/` directory shipped with the * workspace. Tests that need isolation should pass `packagedSeedDir` * explicitly rather than relying on this default. * * Replaces `resolveDefaultSeedDir()` (ADR-068 Decision 2 — single `templates/` * layout; `seed-agents/` directory deleted by T1932). * * @returns Absolute path to the default templates directory. * @task T889 / W2-4 / T1933 */ export declare function resolveDefaultTemplatesDir(): string; /** * @deprecated Use {@link resolveDefaultTemplatesDir} instead. * * Preserved as a shim for one major-version cycle. The `seed-agents/` * directory was deleted by T1932 (ADR-068 Decision 2). Callers that relied * on this function's return value for filesystem operations MUST migrate to * `resolveDefaultTemplatesDir()`. * * @returns Absolute path to the (now-deleted) seed-agents directory. * @task T1933 */ export declare function resolveDefaultSeedDir(): string; /** * Compute the default path to the universal-base `.cant` file. * * Resolution strategy (two-phase, first hit wins): * * **Phase 1 — `require.resolve` (primary, workspace + published parity)** * Uses Node's module resolver to locate `@cleocode/agents/package.json` from * the current file's module graph. This works identically in the workspace * (`packages/core/src/store/` resolving into `packages/agents/`) and in a * globally-installed CLI (`node_modules/@cleocode/core/…` resolving into * `node_modules/@cleocode/agents/`). After resolving the package root the * canonical `cleo-subagent.cant` path is constructed as a sibling of * `package.json`. * * **Phase 2 — relative-path walk (fallback, compile-time layouts)** * When `require.resolve` cannot locate the package (e.g. the package is not * declared as a direct dependency and Node's resolution cannot reach it), a * set of relative candidates is tried covering: * - workspace `src/` layout: `packages/core/src/store/` → `packages/agents/` * - workspace `dist/` layout: `packages/core/dist/store/` → `packages/agents/` * - installed layout (three-level `..`): * `node_modules/@cleocode/core/dist/store/` → `node_modules/@cleocode/agents/` * * Matching `resolveAgentTemplates()` / `resolveMetaAgentsDir()` approach * (T1935) which already has workspace+published parity. * * @returns Absolute path to `cleo-subagent.cant`, or `null` when unresolved. * @task T1241 / D035 / T9037 */ export declare function resolveDefaultUniversalBasePath(): string | null; export {}; //# sourceMappingURL=agent-resolver.d.ts.map