import { type ActAs, type RunContext, type ToolDescriptor, type ToolRegistry } from "../../core/index.js"; import type { Connector } from "../connectors/connector.js"; import { type CapabilityBrief, type ExtractedTool, type OverridesFile, type ToolOverride } from "../formats.js"; import { type ToolSearchMatch, type ToolSearchOptions } from "./search.js"; export interface ActionsRegistry extends ToolRegistry { add(tools: ToolRegistry): void; /** Capability briefs carried by `.vendo/overrides.json` (04 §1). Validated and exposed; consumed by later milestones. */ briefs(): Promise; /** * Runtime tool search (ENG-252): rank the merged, enabled tool surface against * a free-text intent. Disabled tools are excluded (they never enter the loaded * descriptor set), so a hit is always a loadable, guard-bound tool. */ search(query: string, options?: ToolSearchOptions): Promise; /** The per-turn initial loadout: every loaded tool, never an alphabetical * slice of the catalog. */ loadoutSeed(): Promise; /** * The tool menu one SURFACE offers, resolved from `.vendo/overrides.json`'s * `surfaces` block. `undefined` means unrestricted — the surface offers * everything it would have offered before menus existed. * * An explicit `surfaces..tools` wins, in the host's authored order. * Absent, `agent` is unrestricted and `mcp` falls back to the default door * menu: every merged, enabled tool whose post-override `audience` is * `"end-user"` or ungraded — the tools a product's own customer could * legitimately call, which is exactly who is on the far end of an MCP client. * * CURATION, NOT SECURITY. A menu changes what a surface OFFERS; the guard, * `disabled`, and audience exclusions decide what may RUN, and none of them * consult this. A menu entry naming an unknown or disabled tool is therefore * a typo, not a breach: it warns once per boot and is ignored, and the rest * of the menu still applies (a bad label must never take a host down). */ surfaceMenu(surface: "agent" | "mcp"): Promise; /** The brokered-connector toolkit a loaded tool belongs to (undefined for * host tools, compounds, and connectors without per-user connections) — * the lookup behind the pre-guard connect check (discovery discipline, * spec 2026-07-25). */ connectorToolkit(tool: string): Promise<{ connector: string; toolkit: string; } | undefined>; /** The human's override for one tool NAME, from `.vendo/overrides.json` — * answered for names this registry never LISTED too. The long-tail tools * behind `use_service_tool` are reachable only by the broker's own slug, so * the dispatcher grades a slug through here and the authored file is the * last word there exactly as `mergeOverride` makes it for a listed tool. */ toolOverride(tool: string): Promise; } /** CORE-2 (wave 5): `grant` and `mcpConsent` are first-class optional fields * on core's RunContext now — the structural twin this alias used to declare is * gone. The alias survives for existing imports; new code can use RunContext * directly. */ export type ActionsRunContext = RunContext; /** One entry of the wiring-generated registration map (04 §1): the imported * server-action function itself. `never[]` keeps arbitrary host action * signatures assignable; the runtime invokes positionally per the binding's * `params` order. */ export type ServerActionHandler = (...args: never[]) => unknown; interface RegistryConfig { dir?: string; tools?: ExtractedTool[]; connectors?: Connector[]; actAs?: ActAs; /** * 04 §1: the server-action registration map the generated wiring file passes * into `createVendo({ serverActions })`, keyed `"#"`. * Dispatch is direct and in-process — no Next action-id bindings. A * server-action tool whose key is absent fails closed (clear error, no work). */ serverActions?: Record; baseUrl?: string; /** * Whether `baseUrl` is an operator-set, trusted origin. Present-request * credentials (cookie/authorization) are forwarded to a route binding's host * ONLY when the base is trusted. An origin auto-derived from an inbound * request (e.g. the umbrella's zero-config same-origin default) is NOT * trusted: a spoofed Host on any early request would otherwise poison the * base and exfiltrate a later user's forwarded credentials. Defaults to true * so an explicitly-passed baseUrl keeps forwarding. */ baseUrlTrusted?: boolean; /** Umbrella-owned structured warning hook. It fires only when a present host * call has browser auth to forward but the target fails the trusted-origin * rule. Callers should de-duplicate at the composition boundary. */ onPresentCredentialsNotForwarded?: (event: { ctx: RunContext; tool: ToolDescriptor; reason: "untrusted-host-origin" | "cross-origin-binding"; }) => void | Promise; /** * 09-vendo §2 (install-dx wave 1.1): what to do when a present-mode call has * browser auth to forward but the target fails the trusted-origin rule for * "untrusted-host-origin" specifically — NEVER for "cross-origin-binding", * which always stays warn-only (same-origin trust must never extend to a * cross-origin binding). "warn" is today's behavior: fire * `onPresentCredentialsNotForwarded` and run the call unauthenticated. * "fail" runs the hook (the audit warning still records) and then fails the * call closed instead of reaching the host with no credentials — the * umbrella sets this in production so a missing VENDO_BASE_URL surfaces * loudly. Defaults to "warn". */ untrustedOriginPolicy?: "warn" | "fail"; fetch?: typeof fetch; /** Inject the authored overrides doc directly instead of reading * `.vendo/overrides.json` from `dir`. Two callers share this seam: the * unified try surface (Task 15a) passes an in-memory doc for non-file * hosts, and the hosted-config seam (cse lane 3) lets the umbrella pass * cloud-published overrides when there is no local file. Takes precedence * over the file read whole-file (mirrors `tools`/`capabilities`), and the * corrections apply to host and connector tools the same way the dir * read's do (mergeOverride at load). The provider form is resolved ONCE * through the memoized loadHost (boot-once, no hot-swap) and MAY be async * so the umbrella can await a first-request cloud fetch; resolving to * undefined falls back to the `dir` file read. `tools.json` always comes * from `dir`. The resolved doc is validated at load with the authored-file * posture: a malformed doc throws `validation` loudly. */ overrides?: OverridesFile | (() => OverridesFile | undefined | Promise); /** * 04 §6: the guard-bound execution seam every compound step routes through. * The umbrella assigns it AFTER `guard.bind(actions)` — read at execution * time, exactly like `baseUrl`. Absent → compounds return `not-implemented` * and perform no work; there is no second execution path. */ invokeTool?: ToolRegistry["execute"]; } export declare function createActions(config: RegistryConfig): ActionsRegistry; export {};