/** * Atomic agent install pipeline. * * Validates a `.cant` source, computes its SHA-256, copies it to the target * tier directory, and writes an `agent_registry_agents` row + * `agent_registry_agent_skills` junctions in ONE SQLite transaction (post-T11622 * cutover — folds T11578 AC2). On any failure the transaction is rolled back and * the filesystem is rewound to its pre-call state (newly-copied file is * removed if we own it), leaving DB + FS unchanged. * * The install function takes an already-open handle to the GLOBAL consolidated * `cleo.db` (i.e. the target of `openGlobalDb()` in * `agent-registry-accessor.ts`) rather than opening its own, so composite * workflows (seed-install, `cleo agent attach`) can batch multiple installs * under a single DB handle. Project attachment (`conduit.db:project_agent_refs`) * is orthogonal and is not performed here — callers that want to attach a * just-installed agent to the current project should follow up with * `attachAgentToProject(projectRoot, agentId)` from * `agent-registry-accessor.ts`. * * A minimal local `.cant` field-extractor is used instead of depending on * `@cleocode/cant`, which would introduce a circular package dependency * (`@cleocode/cant` already depends on `@cleocode/core`). The extractor * recognises the top-level `agent :` header, the trailing key/value * block, and the specific fields (`role`, `parent`, `skills`) that drive the * v3 registry columns. Anything more elaborate is intentionally out of * scope; downstream validation remains the responsibility of the CANT * linter and the `cleo agent doctor` walk. * * @module agent-install * @task T889 / W2-3 * @epic T889 */ import type { DatabaseSync } from 'node:sqlite'; import type { AgentTier } from '@cleocode/contracts'; /** * Input to {@link installAgentFromCant}. * * @task T889 / W2-3 */ export interface InstallAgentFromCantInput { /** Absolute path to the source `.cant` file to install. */ cantSource: string; /** Target tier: `'global'` writes to {@link getCleoGlobalAgentsDir}; `'project'` writes to `/.cleo/cant/agents/`. */ targetTier: Extract; /** Provenance tag written to `agents.installed_from`. */ installedFrom: 'seed' | 'user' | 'manual'; /** Absolute path to the project root (required when `targetTier === 'project'`). */ projectRoot?: string; /** Override the destination directory for `'global'` installs (defaults to {@link getCleoGlobalAgentsDir}). */ globalCantDir?: string; /** When `true`, overwrite an existing row/file instead of throwing. */ force?: boolean; } /** * Result returned from {@link installAgentFromCant} on success. * * @task T889 / W2-3 */ export interface InstallAgentFromCantResult { /** Business identifier (`agents.agent_id`) of the installed agent. */ agentId: string; /** Absolute destination path after the copy. */ cantPath: string; /** SHA-256 checksum of the copied `.cant` bytes (hex-encoded). */ cantSha256: string; /** Tier the row was installed at. */ tier: AgentTier; /** `true` when this call INSERTED a new row, `false` when it UPDATED an existing row. */ inserted: boolean; /** Skill slugs resolved from the agent's `skills` field. */ skillsAttached: string[]; /** Non-fatal warnings (e.g. unknown skill slugs). */ warnings: string[]; } /** * Install an agent from a `.cant` source file: parse the manifest, copy the * file to the tier-appropriate location, and write the `agents` row + * `agent_skills` junctions in a single transaction. * * Safety properties: * - On parse / validation failure the transaction has not yet begun; the * filesystem is untouched. * - On DB failure inside the transaction, the transaction is rolled back * AND the destination file is removed if this call created it, restoring * the pre-call state. * - `force: false` + an existing row throws `E_AGENT_ALREADY_INSTALLED` * before any file I/O. * * The caller owns the `db` handle lifecycle; this function never calls * `db.close()`. * * @param db - Open handle to global cleo.db (Agent Registry). * @param input - Install options (source path, tier, provenance, flags). * @returns Install result with the persisted agentId, digest, and tier. * @throws When the source is invalid, a duplicate row exists without * `force`, or the DB/filesystem operation fails. * @task T889 / W2-3 */ export declare function installAgentFromCant(db: DatabaseSync, input: InstallAgentFromCantInput): InstallAgentFromCantResult; //# sourceMappingURL=agent-install.d.ts.map