services

One handle per use a system declares in uses, named by the use and typed by its interface. See Services for what a use is.

// on the run — one field per use its system declares in `uses`
run.services.<use>.available: boolean
run.services.<use>.describe(): Description<Interface>
run.services.<use>.<method>(...): Promise<Answer>               // a call
run.services.<use>.<method>(...): AsyncIterable<Event>         // a stream
run.services.<use>.<method>(): Session                         // a session

A system holds only the uses it names in uses; reaching another is an error in the editor, and refused by name if it runs. The contracts themselves are written with interfaces.

available and describe() are state — as they stood when the run began — so any system may read them. Calls, streams and sessions need an answer, so only an async system makes them.

Declaring

// cognet.config.ts
uses: {
    <use>: {
        interface: "<name>@<major>",
        optional?: boolean,          // absent: required
        requires?: { … },            // bounds on what describe() reports
    },
},
suggests: {
    <use>: "<provider package>",
},
FieldMeans
interfacethe contract, by name and major version
optionalthe mind runs without it. A required use with nothing to fill it fails before the mind starts
requiresbounds on the provider's describe() — { context: { min: 100_000 } }, or a value it must equal
suggestsa provider the cognet recommends, fetched at axonl prepare, offered to the agent

available

Whether the use is filled right now. It can change while the mind runs — read it on every run that needs it.

if (!run.services.depth.available) return

describe()

What the granted provider is — never which provider it is. Every interface declares describe(), and what it returns.

const { context, slots } = run.services.main.describe()

A call — chat@1's request, depth-metric@1's estimate

const depth = await run.services.depth.estimate(picture)
const reply = await run.services.main.request({ messages: messages })

Arrays cross as memory, whatever language the provider is in.

A stream — chat@1's stream

for await (const event of run.services.main.stream({ messages: messages })) {
    if (event.type === "text") show(event.text)
}

A session — voice-activity@1's open

A session keeps state between pushes. Push in order; reset starts again; close lets it go. A session is machinery, not a belief — the system that feeds it holds it.

const vad = run.services.vad.open()
const speaking = await vad.push(frame)
await vad.close()

A handle — segment@1's encode

An opaque reference to something the provider keeps. Pass it back to the same use; release it when done.

const encoded = await run.services.segment.encode(picture)
const mask = await run.services.segment.mask(encoded, { point: [120, 80] })
await encoded.release()

A handle ends when it is released, when the provider behind the use changes, or when the run that made it ends — whatever a run still holds when it ends is released for it.

Errors

A refusal carries the provider's reason under a structured code, the same in every language.

ErrorWhen
SERVICE_UNAVAILABLEa call to a use that is not available — ask available first
SERVICE_REFUSEDthe provider refused: a policy, a limit, a cost
SERVICE_FAILEDthe provider failed to answer
SERVICE_INVALIDan argument the interface does not accept
SERVICE_HANDLE_ENDEDa handle used after it was released, its provider changed, or its run ended
SERVICE_LIMITthe agent's grant refused it: a cost or rate limit was reached