@netscript/plugin-ai-core
The contract-only core for the NetScript AI plugin: the oRPC /v1/ai route surface — an
SSE-framed chat stream plus models, tools/{name}, embed, and transcribe — that a
connector implements and the typed client calls. This page is generated from the package's
public surface with deno doc (US-2). For the full index of packages and plugins return to the
reference overview.
The package ships zero service implementation (Archetype 1 — Small Contract). It declares the
route shapes and their Zod IO vocabulary; a connector (@netscript/plugin-ai) binds the contract
to a host and the @netscript/fresh/ai client generates a typed caller from it. Every route schema
mirrors an engine type from @netscript/ai/contracts, and a compile-time drift guard
(z.ZodType<EngineType> = schema) asserts the mirror cannot silently diverge from the domain
contract.
Entrypoints
The package publishes two entrypoints. Each is generated from its own deno doc surface.
| Export | Entrypoint | Purpose |
|---|---|---|
@netscript/plugin-ai-core |
./mod.ts |
Contract handles, router helpers, and route IO types (documented below). |
@netscript/plugin-ai-core/contracts/v1 |
./src/contracts/v1/mod.ts |
Full contract surface: the Zod *Schema validators plus the engine-derived vocabulary types. |
The root path curates to the handles and IO types a typical consumer imports (staying within the
≤20-export doctrine budget); the ./contracts/v1 subpath exposes the complete schema and
engine-vocabulary set a connector wires.
Root surface (@netscript/plugin-ai-core)
Contract handles and router helpers
| Symbol | Kind | Description |
|---|---|---|
aiContract |
variable | AI service contract definition for client generation (the real, fully-inferred oRPC contract router — no erasure cast). |
aiContractV1 |
variable | The implemented, context-bindable v1 contract (implement(definition)); .$context<Ctx>() returns the precisely-typed router implementer. |
createAiRouter |
function | Bind a complete in-process AI implementation to the v1 contract, returning the oRPC handlers for each route. |
AiRouterContext |
type alias | Request context accepted by the generated AI router (Record<PropertyKey, unknown>). |
ContextualAiRouter |
type alias | The router implementer returned by aiContractV1.$context<TContext>(). |
AiRouteHandler |
type alias | Handler function type for a single named route under a bound context. |
AiRouterImplementation |
interface | Per-route handler functions that implement every route in aiContractV1. |
BoundAiRoute |
type alias | The bound handler type produced by router.<route>.handler(...). |
BoundAiRouter |
interface | Contract-bound route handlers returned by createAiRouter. |
Route IO and capability types
| Symbol | Kind | Description |
|---|---|---|
AiContract / AiContractDefinition / AiContractV1 |
type alias | The fully-typed v1 contract shape and its context-binding implementer type. |
AiRouter |
type alias | The context-bound AI router implementer (ReturnType<typeof aiContractV1.$context<...>>). |
AiCapabilities |
interface | The capabilities document returned by the mandatory describe route (a named alias of PluginCapabilities). |
ChatInput |
type alias | Input accepted by the chat streaming endpoint. |
ChatChunk |
type alias | A single streamed chat frame — alias of the engine AgentChunk union. |
ModelsResponse |
type alias | Response returned by the models endpoint. |
ToolInvokeInput / ToolInvokeResponse |
type alias | Input and response for the tools/{name} invocation endpoint. |
EmbedInput / EmbedResponse |
type alias | Input and response for the embed endpoint. |
TranscribeInput / TranscribeResponse |
type alias | Input and response for the transcribe endpoint. |
Full contract surface (@netscript/plugin-ai-core/contracts/v1)
The subpath re-exports both contract handles (aiContract, aiContractV1), every route IO type
above, and the following additional symbols.
Zod IO schemas
Each schema is a named, explicitly-annotated Zod validator (annotated z.ZodType<EngineType> so the
module fails to compile if it drifts from the engine contract it mirrors). A connector validates
inbound bodies and outbound frames against these.
| Symbol | Kind | Validates |
|---|---|---|
ChatInputSchema |
variable | chat request body (ChatInput). |
ChatChunkSchema |
variable | A single streamed chat frame (ChatChunk). |
ModelsInputSchema |
variable | Optional models filter ({ provider? }). |
ModelsResponseSchema |
variable | models response ({ models: ModelDescriptor[] }). |
ToolInvokeInputSchema |
variable | tools/{name} request body. |
ToolInvokeResponseSchema |
variable | tools/{name} response. |
EmbedInputSchema |
variable | embed request body. |
EmbedResponseSchema |
variable | embed response. |
TranscribeInputSchema |
variable | transcribe request body. |
TranscribeResponseSchema |
variable | transcribe response. |
Engine-derived vocabulary types
The subpath re-exports the @netscript/ai/contracts streaming vocabulary verbatim, so a connector
references the same domain types the schemas mirror without reaching into the engine package:
AgentChunk, AudioContentPart, CompletionTokensDetails, ContentModality, ContentPart,
ContentSource, DataContentSource, DocumentContentPart, DoneChunk, ErrorChunk,
ImageContentPart, Message, MessageChunk, MessageContent, MessageRole,
ModelCapabilities, ModelDescriptor, ModelId, ModelRef, ModelSelector, ModelsInput,
PromptTokensDetails, ProviderUsageDetails, TextChunk, TextContentPart, ToolCall,
ToolCallChunk, ToolCallState, ToolDescriptor, ToolResult, ToolResultChunk,
ToolResultState, UrlContentSource, Usage, UsageChunk, UsageCostBreakdown,
VideoContentPart, plus the AiContractDefinitionShape structural type.
The /v1/ai route surface
Route paths are relative; the /v1/ai prefix is applied where the service host mounts the router.
The contract layers five plugin-specific routes onto the base seam describe route.
| Route | Method | Path | Input | Output |
|---|---|---|---|---|
chat |
POST | /chat |
ChatInput |
SSE eventIterator<ChatChunk> |
models |
GET | /models |
ModelsInput? |
ModelsResponse |
invokeTool |
POST | /tools/{name} |
ToolInvokeInput |
ToolInvokeResponse |
embed |
POST | /embed |
EmbedInput |
EmbedResponse |
transcribe |
POST | /transcribe |
TranscribeInput |
TranscribeResponse |
describe |
GET | /describe |
— | AiCapabilities |
chat input
ChatInput carries the request the connector forwards to its provider adapter.
| Field | Type | Notes |
|---|---|---|
model |
ModelRef |
A "provider:model" string or a { provider, model } selector. |
messages |
readonly Message[] |
The conversation transcript. |
tools |
readonly ToolDescriptor[] (optional) |
Tool descriptors offered to the model. |
system |
string (optional) |
System prompt. |
temperature |
number (optional) |
Constrained 0–2 by the schema. |
maxTokens |
number (optional) |
Positive integer. |
metadata |
Record<string, unknown> (optional) |
Opaque per-request metadata. |
SSE chat contract (durable-CHAT)
chat is the streaming centerpiece. Its output is not a request/response pair but an oRPC
eventIterator whose element type is ChatChunk — an alias of the engine AgentChunk union.
Because the contract output type is an async event-iterator, a connector's chat handler must be an
async generator that yields frames; a buffered single-response implementation does not type-check.
Recoverable and terminal errors surface in-stream as the error chunk rather than as an oRPC
error, matching the engine's streaming model — so the chat route (built with oc.route, not the
base contract) carries an empty error map.
Event (chunk) shapes
Each streamed frame is one of seven discriminated shapes, keyed on type:
type |
Payload | Meaning |
|---|---|---|
text |
{ delta: string } |
Incremental assistant text appended this tick. |
tool-call |
{ toolCall: ToolCall } |
A tool invocation requested by the model. |
tool-result |
{ result: ToolResult } |
The result of an executed tool, fed back into the loop. |
message |
{ message: Message } |
A fully-assembled message committed to the transcript. |
usage |
{ usage: Usage } |
Token usage for the step or run. |
error |
{ error: string, cause?: unknown } |
A recoverable or terminal error surfaced through the stream. |
done |
{ usage?: Usage } |
Terminal frame closing the run, optionally carrying final usage. |
The contract declares frame shapes, not wire headers.
@netscript/plugin-ai-coreis a transport-agnostic oRPC contract: thetext/event-streamwire framing (per-framedata:lines, the response content type) is produced by the oRPC handler at the host mount point when it serves aneventIteratoroutput, not declared in this package. This page documents the event shapes the stream carries; the HTTP framing belongs to the connector's service host.
Base-contract extension
aiContract spreads BASE_PLUGIN_CONTRACT_ROUTES from
@netscript/plugin/contract-base verbatim, so it inherits the
mandatory describe route (GET /describe) unchanged. describe returns a
marketplace-discoverable AiCapabilities document — a named alias of PluginCapabilities:
| Field | Type | Description |
|---|---|---|
pluginName |
string |
Canonical plugin package name, e.g. @netscript/plugin-ai. |
contractVersions |
readonly string[] |
Contract version identifiers served by the plugin. |
routeGroups |
readonly string[] |
Route group names exposed by the plugin. |
capabilities |
readonly string[] |
Capability tags advertised by the plugin. |
The non-streaming routes (models, invokeTool, embed, transcribe, describe) converge onto
the shared plugin error vocabulary from BASE_PLUGIN_ERRORS:
| Code | HTTP status | Default message |
|---|---|---|
NOT_FOUND |
404 | Resource not found |
VALIDATION_ERROR |
422 | Validation failed |
INTERNAL |
500 | Internal server error |
Binding the contract (aiContractV1 / AiRouter)
A connector binds its host request context and implements each route handler. The precise per-route IO types check every handler; there is no erasure cast on the contract or the implementer.
aiContractV1.$context<TContext>() returns the contract-bound implementer — AiRouter is the
type alias naming its default (empty-context) shape, and ContextualAiRouter<TContext> the
context-parameterized one. The implementer is not an assembled handler map: each route on it
exposes a .handler() call that binds one implementation function.
import { aiContractV1 } from '@netscript/plugin-ai-core';
// Bind the host request context; each route is then implemented individually.
const router = aiContractV1.$context<{ requestId: string }>();
// The `chat` output is an event-iterator, so the handler MUST stream frames.
const chat = router.chat.handler(async function* ({ input }) {
for (const message of input.messages) {
yield { type: 'text' as const, delta: `echo: ${JSON.stringify(message.content)}` };
}
yield { type: 'done' as const };
});
// models / invokeTool / embed / transcribe / describe bind the same way.
Assembling all six bound routes by hand is what createAiRouter exists to avoid: it wraps
aiContractV1.$context(), binds a complete per-route implementation (AiRouterImplementation)
in one call, and returns the bound handler map (BoundAiRouter):
import { createAiRouter } from '@netscript/plugin-ai-core';
const router = createAiRouter({
describe: () => ({
pluginName: '@netscript/plugin-ai',
contractVersions: ['v1'],
routeGroups: ['ai'],
capabilities: ['chat'],
}),
async *chat() {
yield { type: 'done' as const };
},
models: () => ({ models: [] }),
invokeTool: () => ({ content: 'No tools registered.', state: 'error' as const }),
embed: () => ({ embeddings: [] }),
transcribe: () => ({ text: '' }),
});
Validating IO on the ./contracts/v1 subpath
The Zod validators let a connector parse an inbound request body against the contract and validate each frame before forwarding it down the stream:
import { ChatChunkSchema, ChatInputSchema } from '@netscript/plugin-ai-core/contracts/v1';
// Parse an inbound request body against the streamed-chat input contract.
const request = ChatInputSchema.parse({
model: 'anthropic:claude-sonnet-4-5',
messages: [{ role: 'user', content: 'Stream me a haiku.' }],
});
// Validate each frame a provider emits before forwarding it down the SSE stream.
const chunk = ChatChunkSchema.parse({ type: 'text', delta: 'silent...' });
console.log(request.model, chunk.type);
How the AI plugin stack consumes this contract
- Connector (
@netscript/plugin-ai) — re-exports this contract surface from its own./contracts/v1(export * from '@netscript/plugin-ai-core/contracts/v1') and declares the loader on its manifest (withContractVersions), so a host binds the AI plugin against a single pinned/v1/aicontract. It bindsaiContractV1.$context<Ctx>()and implements each route against a provider adapter. @netscript/fresh/aiclient — importsaiContractand generates a typed caller, so a Fresh island streamschatchunks and callsmodels/embed/transcribewith full type inference and no hand-written request types.- Marketplace tooling — reads the
describeroute'sAiCapabilitiesdocument to introspect a running AI plugin without parsing source.
Back to the reference overview.