UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

83 lines (82 loc) • 4.57 kB
import type { TSchema } from "typebox"; import type { AgentTool } from "../internal/harness-types.js"; import type { ProtocolId } from "../core/protocol-table.js"; /** The protocol slice comes from the protocol table — adding a protocol extends ProtocolId and this * union follows automatically (the origin projection is table-driven, prepare-task). */ export type ToolOrigin = "core" | "caller" | "synthetic" | ProtocolId; export interface ToolContractDescriptor { /** Stable contract identity, e.g. `core.read@2`. Versioned independently of the wire name. */ contractId: string; /** Declared behavior revision (protocol §7.1: declarative — hashing a JS closure proves nothing). */ implementationRevision: string; wireName: string; /** Live deployment aliases (ToolSpec.aliases) — dispatchable alternate wire names. Retired-name * replay aliases are gone (RB-476-A, 5.0.0): a persisted old name misses loudly instead. */ aliases?: string[]; origin: ToolOrigin; /** Structure-only schema digest (descriptions/titles/examples stripped) — presentation-invariant. */ shapeDigest: string; /** Full materialized schema digest (incl. descriptions) — the model-visible wire face. */ wireSchemaDigest: string; /** The presentation card in effect. v1: `builtin:<wireName>` (the factory's inline text). */ cardId: string; } /** * Manifest v2 row (protocol §5.4) — {@link projectToolManifest}'s per-tool projection of * {@link ToolContractDescriptor} onto the wire-visible manifest plane. REF-D25 (2026-08): * this is an intentional projection, not a subset alias — it drops `origin` and normalizes * `aliases` from optional to required (empty array when there are none), so it cannot be * expressed as `Pick<ToolContractDescriptor, ...>`. `core/trace.ts` (TraceEvent's * `prompt.assembled` manifest) and `core/runner/prepare-task.ts` (`Prepared.promptManifest.tools`) * both reference this same type — the value flows prepare-task.ts → runtask.ts → trace.ts * untouched, so a shape drift between those two sites would make a real call-shape change * invisible to replay/audit (the same shapeDigest-collision class documented above on * `stripPresentation`). */ export interface ToolManifestRow { wireName: string; aliases: string[]; contractId: string; implementationRevision: string; cardId: string; shapeDigest: string; wireSchemaDigest: string; } export declare function computeShapeDigest(schema: TSchema | undefined): string; export declare function computeWireSchemaDigest(schema: TSchema | undefined, description: string): string; export interface AttachContractInput { contractId: string; implementationRevision: string; origin: ToolOrigin; aliases?: string[]; } /** Attach a contract descriptor to a materialized tool (factory exit point). Digests are computed * from the tool's REAL mounted schema/description, so they always reflect the wire face. */ export declare function attachToolContract(tool: AgentTool, input: AttachContractInput): void; /** * Resolve a tool's descriptor. Undeclared tools get a deterministic legacy descriptor derived from * their shape (`legacy:<shapeDigest>`), origin per the mount channel — an external tool NEVER * inherits a core contract by name (§7.2: don't guess contracts from names). */ export declare function getToolContract(tool: AgentTool, fallbackOrigin?: ToolOrigin): ToolContractDescriptor; export declare function hasToolContract(tool: AgentTool): boolean; /** Manifest projection for the final mounted tool set (manifest v2 `tools`, protocol §5.4). */ export declare function projectToolManifest(tools: readonly AgentTool[], originOf?: (tool: AgentTool) => ToolOrigin): ToolManifestRow[]; /** * RB-15: wire-identity collision audit over the FINAL mounted tool set. * Rules (plan §6.2 tool row): * - an ALIAS colliding with another tool's canonical name or alias = FATAL (silent misdispatch * of durable replays would follow — never ship such a mount); * - two tools sharing a CANONICAL name = the historic deliberate yield (a caller tool may shadow * a built-in; the harness Map keeps the later mount) — NOT fatal, but returned as an explicit * exemption record so the mount site can surface it (observable, never silent). */ export declare function auditToolCollisions(tools: readonly AgentTool[]): { yields: Array<{ name: string; }>; aliasCollisions: Array<{ alias: string; owners: string[]; }>; };