interfaces

An interface is the contract between a use and whatever fills it. The standard library covers the common ones — chat@1, depth-metric@1. When nothing standard fits, a cognet writes its own in interfaces/. See Services for where it fits.

// interfaces/<name>@<major>.interface.ts — always TypeScript, generated into every language
export interface <Name> {
    describe(): { … }                              // required — what a provider reports of itself
    <method>(...): Promise<Answer>                  // a call
    <method>(...): AsyncIterable<Event>             // a stream
    <method>(): Session<Push, Answer>               // a session
}
export type <Kept> = Handle<"<Kept>">               // an opaque handle, kept by the provider
PartMeans
the file namethe interface's name and major version: scene@1.interface.ts is scene@1
the exported interfacethe contract — exactly one per file
describe()the provider's features and limits — the only thing a use can require
the methodswhat a use may call, each one of four kinds

Its full name is the cognet's: scene@1 in @cody/terry is @cody/terry/scene@1, published with it.

Methods

The return type says what kind of call a method is. Nothing else does.

ReturnsKindA use calls it
Promise<T>a call — one in, one outawait run.services.x.m(…)
AsyncIterable<T>a stream — one in, events outfor await (const e of run.services.x.m(…))
Session<Push, Answer>a session — pushes that share staterun.services.x.m().push(…)
Handle<"Name">an opaque handle — inside a Promisepassed back to the same use
export interface Scene {
    describe(): { maxSurfaces: u32; gpu: boolean }

    /** Encodes what is in view. The costly part — done once, asked about many times. */
    encode(picture: Picture, depth: DepthMap): Promise<SceneCode>

    /** The surface under a point of the picture. */
    surface(scene: SceneCode, at: [f64, f64]): Promise<Surface>

    /** Follows the scene as the camera moves: push each picture, get what changed. */
    follow(): Session<Picture, Changes>
}

export type SceneCode = Handle<"SceneCode">

Arguments and answers follow the rules of components: plain data, sized numbers, typed arrays — which cross as memory. They may be imported from the cognet's components, or from another interface.

import type { Picture } from "../types/sense/sense.components"
import type { DepthMap } from "@arcforge/cognet/interfaces/depth-metric@1"

The doc comment on each method is the contract's meaning — units, frames, what an empty answer holds. Write it; a provider has nothing else to go on.

describe()

Every interface declares it, with no arguments, returning plain data. It is how a provider says what it is, and the only thing a use can constrain:

// cognet.config.ts
uses: {
    scene: { interface: "./interfaces/scene@1", requires: { maxSurfaces: { min: 64 }, gpu: true } },
},
requires on a fieldMeans
a valuedescribe() must report exactly it
{ min, max }a number within the bounds
{ includes: [...] }a list holding each of these

A grant whose provider fails requires is refused at axonl prepare, or when the agent boots.

Generated

The interface is written once and generated into each language that uses it, as components are.

// .cognet/ — ambient
run.services.scene.describe(): { maxSurfaces: number; gpu: boolean }
run.services.scene.encode(picture: Picture, depth: DepthMap): Promise<SceneCode>
run.services.scene.follow(): Session<Picture, Changes>

A provider in any language implements the same generated shape.

Conformance

Beside each interface, the cases any provider of it must answer. axon conform fills every use an agent's cognet declares as its boot would, and runs each interface's cases against what filled it.

interfaces/
├── scene@1.interface.ts
└── scene@1.conformance/
    ├── cases.json
    └── table.rgb            # raw RGB bytes, for a picture the cases name
[
    {
        "name": "a point picks out the surface under it",
        "call": "surface",
        "given": {
            "scene": { "$call": "encode", "given": { "picture": { "$picture": { "width": 640, "height": 480, "file": "table.rgb" } } } },
            "at": [320, 400]
        },
        "expect": { "normal": [0, 0, 1], "distance": 0.82 },
        "within": { "normal": 0.05, "distance": 0.02 }
    }
]
FieldMeans
call / streamthe method — a stream's answers are checked as { events, last }
givenits arguments, by parameter name
expectthe answer, field by field
withinthe tolerance on each number checked exactly; absent, it must match exactly

A case writes its inputs as values, or as:

InputIs
{ "$picture": { width, height, fill: [r, g, b] } }a picture of one colour
{ "$picture": { width, height, file } }a picture from raw RGB bytes beside the cases
{ "$u8": [...] }, { "$f32": [...] }a typed array
{ "$call": "<method>", "given": { … } }another call's answer — a handle, usually

And checks an answer's fields as values, or with:

RulePasses when
{ "min": n, "max": n }the number is within the bounds
{ "oneOf": [...] }it is one of these
{ "type": "string" }it is of this type
{ "length": n } · { "length": "width*height" } · { "length": { "min": 1 } }a list or array is this long — or the product of the answer's own fields
{ "each": <rule> }every item passes

Write the cases a provider would most likely get wrong — the conventions, not the easy path. A case for any provider can't know what a particular model sees; it can know the units, the shape, and what a value may never be.

Versions

A major version is a promise never to break it. A breaking change is a new file — scene@2 beside scene@1 — and uses move to it one at a time. Adding a method, or a field to describe(), is not breaking; changing or removing one is.

Errors

ErrorWhen
INTERFACE_NOT_FOUNDa use names an interface that doesn't exist
COGNET_SCHEMA_INVALIDno describe(), more than one interface in a file, a method returning no kind, or a type no language can hold
INTERFACE_VALUE_INVALIDan argument or answer a component couldn't hold
INTERFACE_BROKENa published major version changed incompatibly