UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

89 lines (88 loc) • 6.21 kB
/** * Protocol namespace table (roadmap 2026-08-02, the non-additive precondition for a second protocol). * * design/108 ① established ONE namespaced family — `mcp__<server>__<tool>` — and six call sites grew * their own hard-coded `"mcp__"` reads (origin projection, grouped display, permission-rule lanes, * the `__` reservation message). Each new protocol would need a twin branch at every site. This table * makes "which protocol owns this tool name" a lookup: a second protocol (e.g. A2A) lands by * APPENDING one entry, and every table-driven site picks it up unchanged. * * Dependency-free by design (pure string logic) — `mcp.ts` re-exports {@link MCP_NAMESPACE}'s prefix * as its public `MCP_PREFIX` face, never the other way around. The MINTING pipeline (charset * normalization + the assembled-name budget, which needs a digest) lives in `protocol-naming.ts`, which * imports this module's type; the dependency never runs the other way, so this file stays dependency-free. */ /** The ids of every protocol the engine speaks. Doubles as the protocol slice of the tool-manifest * `ToolOrigin` union — adding a protocol extends BOTH (tsc pairs the change). */ export type ProtocolId = "mcp" | "a2a"; /** The (peer, tool) pair a namespaced wire name decomposes into. */ export interface ProtocolPeerTool { peer: string; tool: string; } /** * One tool-name namespace owned by an external protocol (design/108 ① generalized). * * `P` carries the namespace's prefix as a LITERAL type so a public face re-exporting it (mcp.ts's * `MCP_PREFIX`) keeps `"mcp__"` rather than widening to `string` — a widened prefix compiles fine * here while breaking every downstream literal-typed position, and the export-surface snapshot * (name → kind) cannot see that class of change at all. */ export interface ProtocolNamespace<P extends string = string> { /** Stable protocol id — doubles as the tool-manifest origin tag (e.g. `"mcp"`). */ readonly id: ProtocolId; /** Global marker prefix (`mcp__`). A name in this namespace starts with it; no caller tool name may * contain the `__` separator at all (prepare-task's reservation enforces the umbrella). */ readonly prefix: P; /** * Compose the wire name for a (peer, tool) pair. * * NAME SHAPE ONLY — this is the namespace's spelling contract (prefix + separator placement), NOT the * full minting pipeline: sanitizing and length-clamping the segments is `protocol-naming.ts`'s * `mintNamespacedToolName`, which every protocol registering real tools goes through. Composing a raw, * unsanitized pair here yields a syntactically valid name that no mint would have produced — use this * leg for a name whose segments are already known-good (a test, a display form), the mint otherwise. * * NAMING CONSTRAINTS (enforced by a THROW, not a silent mangle): `peer` and `tool` must be non-empty and * `peer` must not contain the `__` separator. A separator inside `peer` makes the name un-round-trippable * — {@link parse} splits at the FIRST separator, so `("prod__db", "query")` reads back as peer `"prod"`, * tool `"db__query"` — i.e. a name that silently denotes a different peer than the caller asked for. * `tool` may contain the separator (it is the trailing remainder and round-trips verbatim). */ makeName(peer: string, tool: string): string; /** Parse a wire name; `undefined` when the name is not in this namespace (or malformed inside it — * empty peer segment or empty tool segment both count as malformed). */ parse(name: string): ProtocolPeerTool | undefined; /** Collapsed display key for grouped listings (`mcp__<server>__*`); identity for a malformed name. */ displayGroupKey(name: string): string; } declare const MCP_PREFIX_NAME: "mcp__"; /** A2A's marker prefix. `a2a__<peer>__<skill>`: `peer` is the remote agent (whatever id the deployment * knows it by), `skill` the capability that agent advertises — the same two-segment shape MCP uses, so * every table-driven site (origin projection, grouped display, the caller-name reservation) reads it * unchanged. Deliberately NOT a prefix of, nor prefixed by, `mcp__`. */ declare const A2A_PREFIX_NAME: "a2a__"; export declare const MCP_NAMESPACE: ProtocolNamespace<typeof MCP_PREFIX_NAME>; /** * The A2A tool namespace (roadmap 2026-08-03, appended for the A2A client/server work). * * NAME LAYER ONLY: this entry decides how an A2A tool name is SPELLED and how it decomposes. The * client leg (`a2a.ts`, `materializeA2aTools`) mints live names in this namespace since v5.3.0; the * table entry predates it so that every table-driven site (`protocolOf`, the grouped display key, the * `ToolOrigin` union) needed no change when the leg landed — and needs none for the server-side leg. */ export declare const A2A_NAMESPACE: ProtocolNamespace<typeof A2A_PREFIX_NAME>; /** Every protocol namespace the engine knows. APPEND-ONLY: adding a protocol must not change how any * existing name resolves — prefixes must stay pairwise non-overlapping (pinned in the table test). */ export declare const PROTOCOL_TABLE: readonly ProtocolNamespace[]; /** The namespaced-name shapes the protocol table currently owns, rendered for the two messages that have * to name them (the caller-name reservation and the policy audit's unprefixed-name arm). Read from the * table rather than written out: a message that hard-codes ONE protocol becomes wrong — while staying * green — the moment a second one is appended, and both messages tell a deployment what to write. */ export declare const NAMESPACED_NAME_SHAPES: string; /** Whether `name` carries the namespace separator — reserved for protocol tool names: a deployment tool's name or alias * carrying it is refused, and a rule name carrying it with no protocol prefix names no tool. The one spelling of the test. */ export declare function carriesNamespaceSeparator(name: string): boolean; /** The namespace owning `name`, or `undefined` for a caller/first-party tool. */ export declare function protocolOf(name: string): ProtocolNamespace | undefined; export {};