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>",
},
| Field | Means |
|---|---|
interface | the contract, by name and major version |
optional | the mind runs without it. A required use with nothing to fill it fails before the mind starts |
requires | bounds on the provider's describe() — { context: { min: 100_000 } }, or a value it must equal |
suggests | a 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.
| Error | When |
|---|---|
SERVICE_UNAVAILABLE | a call to a use that is not available — ask available first |
SERVICE_REFUSED | the provider refused: a policy, a limit, a cost |
SERVICE_FAILED | the provider failed to answer |
SERVICE_INVALID | an argument the interface does not accept |
SERVICE_HANDLE_ENDED | a handle used after it was released, its provider changed, or its run ended |
SERVICE_LIMIT | the agent's grant refused it: a cost or rate limit was reached |