Skip to content

extensions

@fungi.computer/shiitake


@fungi.computer/shiitake / extensions

Present only inside a core-run read (projection, run tools, hooks).

  • TagClassShape<"@fungi.computer/shiitake/ExtensionRead", Readonly<{ extensionId: string & Brand<"ExtensionId">; }>, this>

new ExtensionRead(_): ExtensionRead

never

ExtensionRead

Context.Tag( "@fungi.computer/shiitake/ExtensionRead", )<ExtensionRead, Readonly<{ extensionId: ExtensionId }>>().constructor


Present only inside a core-run write transaction (hooks, commands, transact). Core runs these Effects synchronously inside the owner transaction; an asynchronous boundary is a defect that stops the extension.

  • TagClassShape<"@fungi.computer/shiitake/ExtensionTx", Readonly<{ extensionId: string & Brand<"ExtensionId">; }>, this>

new ExtensionTx(_): ExtensionTx

never

ExtensionTx

Context.Tag( "@fungi.computer/shiitake/ExtensionTx", )<ExtensionTx, Readonly<{ extensionId: ExtensionId }>>().constructor


Durable record failure: stored or supplied values broke their schema or bounds.

  • YieldableError<this> & object & Readonly<{ reason: "decode" | "encode" | "bound" | "unavailable"; message: string; }>

new ExtensionStateError(args): ExtensionStateError

"decode" | "encode" | "bound" | "unavailable"

string

ExtensionStateError

Data.TaggedError( “ExtensionStateError”, )<{ reason: “decode” | “encode” | “bound” | “unavailable”; message: string; }>.constructor

readonly reason: "decode" | "encode" | "bound" | "unavailable"

Data.TaggedError( "ExtensionStateError", ).reason

message: string

Data.TaggedError( "ExtensionStateError", ).message


The door refused an extension admission.

  • YieldableError<this> & object & Readonly<{ reason: "invalid" | "conflict" | "closed"; message: string; }>

new AdmissionError(args): AdmissionError

"invalid" | "conflict" | "closed"

string

AdmissionError

Data.TaggedError(“AdmissionError”)<{ reason: “invalid” | “conflict” | “closed”; message: string; }>.constructor

readonly reason: "invalid" | "conflict" | "closed"

Data.TaggedError("AdmissionError").reason

message: string

Data.TaggedError("AdmissionError").message


Typed rejection of one extension command, returned to its caller.

  • YieldableError<this> & object & Readonly<{ reason: "invalid" | "conflict" | "not_found" | "rejected"; message: string; }>

new ExtensionCommandError(args): ExtensionCommandError

"invalid" | "conflict" | "not_found" | "rejected"

string

ExtensionCommandError

Data.TaggedError( “ExtensionCommandError”, )<{ reason: “invalid” | “conflict” | “not_found” | “rejected”; message: string; }>.constructor

readonly reason: "invalid" | "conflict" | "not_found" | "rejected"

Data.TaggedError( "ExtensionCommandError", ).reason

message: string

Data.TaggedError( "ExtensionCommandError", ).message


Fail a hook with this (or die) to stop this extension with a visible reason.

  • YieldableError<this> & object & Readonly<{ reason: string; }>

new ExtensionStop(args): ExtensionStop

string

ExtensionStop

Data.TaggedError("ExtensionStop")<{ reason: string; }>.constructor

readonly reason: string

Data.TaggedError("ExtensionStop").reason


Typed activation failure; the extension is stopped with this message.

  • YieldableError<this> & object & Readonly<{ message: string; }>

new ExtensionActivationError(args): ExtensionActivationError

string

ExtensionActivationError

Data.TaggedError( "ExtensionActivationError", )<{ message: string }>.constructor

message: string

Data.TaggedError( "ExtensionActivationError", ).message

ExtensionId = Schema.Schema.Type<typeof ExtensionId>>

ASCII extension identity shared by the server definition and its clients.


ExtensionRevision = string & Brand.Brand<"ExtensionRevision">>

sha256:<64 hex> derived by core from a definition; never authored.


ExtensionFingerprint = string & Brand.Brand<"ExtensionFingerprint">>

sha256:<64 hex> over one revision and its sorted granted capabilities.


Json = null | boolean | number | string | readonly Json[] | Readonly<{[key: string]: Json; }>

JSON value accepted as hashed extension configuration.


ExtensionCapabilityV1 = "state" | "settled" | "admit" | "timers" | "commands" | "projection" | "run_tools" | "compaction_handoff"

Closed, versioned, feature-free capability vocabulary.


ExtensionManifestV1 = Readonly<{ protocol: "shiitake.extension/v1"; id: ExtensionId; version: string; requests: readonly ExtensionCapabilityV1[]; }>

Static identity and requested capabilities of one v1 extension.


ExtensionGrantV1 = Readonly<{ id: ExtensionId; capabilities: readonly ExtensionCapabilityV1[]; }>

Host decision: the exact subset of requested capabilities it grants.


HookError = ExtensionStop | ExtensionStateError | AdmissionError

Failures a background hook may return; each one stops only its extension.


CollectionSpecV1<A, I> > = Readonly<{ schema: Schema.Schema<A, I>>; key: (value) => string; index?: (value) => string | null; }>

One namespaced collection: its row schema is the only stored-row decoder.

A

I


AnyCollectionSpecV1 = Readonly<{ schema: Schema.Schema.AnyNoContext; key: (value) => string; index?: (value) => string | null; }>

Existential collection spec stored by core.


CollectionsV1 = Readonly<Record<string, AnyCollectionSpecV1>>>>

Collections declared by one extension.


StoredRecordV1 = Readonly<{ collection: string; key: string; value: Json; }>

One stored record as seen by a pure migration.


StateSpecV1<C> > = Readonly<{ version: number; collections: C; migrations: Readonly<Record<number, (row) => readonly StoredRecordV1[]>>; }>

Versioned state declaration; core runs pure row migrations at activation.

C extends CollectionsV1


CollectionV1<A> > = Readonly<{ get: (key) => Effect.Effect<Option.Option<A>>, ExtensionStateError, ExtensionRead>>; page: (query) => Effect.Effect<readonly A[], ExtensionStateError, ExtensionRead>>; put: (value) => Effect.Effect<void, ExtensionStateError, ExtensionTx>>; remove: (key) => Effect.Effect<void, ExtensionStateError, ExtensionTx>>; }>

Typed access to one collection inside a core transaction.

A


StateHandleV1<C> > = { readonly [K in keyof C]: CollectionV1<Schema.Schema.Type<C[K]["schema"]>> }

Typed handles for every declared collection.

C extends CollectionsV1


RunOutcomeV1 = "completed" | "failed" | "cancelled" | "interrupted" | "outcome_unknown"

Watchdog settlement or exact Woodstock terminal of one run.


RunOriginV1 = Readonly<{ extensionId: ExtensionId; correlation: string | null; }>

Extension that admitted a run, with its stored correlation.


SettledRunV1 = Readonly<{ sequence: number; sessionId: string; workId: string; outcome: RunOutcomeV1; terminal: RunOutcomeV1 | "missing"; origin: RunOriginV1 | null; }>

One settled run joined by core with its terminal and admission origin.


SettledPageV1 = readonly SettledRunV1[]

1..64 records, ascending, each delivered to each running extension once.


AdmitInputV1 = Readonly<{ sessionId: string; prompt: Readonly<{ text: string; requestId: string; delivery: "followUp" | "steer"; }>; when: "now" | "idle"; correlation?: string; }>

One prompt admitted through the accepted-command door.


AdmitResultV1 = Readonly<{ _tag: "Accepted"; commandId: string; delivery: "new" | "replay"; }> | Readonly<{ _tag: "NotReady"; }>

Door outcome for one extension admission.


TimersV1 = Readonly<{ set: (key, dueAt) => Effect.Effect<void, ExtensionStateError, ExtensionTx>>; cancel: (key) => Effect.Effect<void, ExtensionStateError, ExtensionTx>>; read: (key) => Effect.Effect<Option.Option<number>>, ExtensionStateError, ExtensionRead>>; }>

Durable per-extension wake timers (the scheduling substrate).


FiredTimerV1 = Readonly<{ key: string; dueAt: number; firedAt: number; }>

A fired timer; its row is consumed unless the handler sets it again.


CommandSpecV1<S, I, II, O, OI> > = Readonly<{ scope: S; input: Schema.Schema<I, II>>; output: Schema.Schema<O, OI>>; }>

One command’s scope and its encoded input and output schemas.

S extends "session" | "agent"

I

II

O

OI


AnyCommandSpecV1 = Readonly<{ scope: "session" | "agent"; input: Schema.Schema.AnyNoContext; output: Schema.Schema.AnyNoContext; }>

Existential command spec.


CommandsV1 = Readonly<Record<string, AnyCommandSpecV1>>>>

Commands declared by one extension contract.


CommandHandlersV1<C> > = { readonly [K in keyof C]: (call: Readonly<{ sessionId: C[K][“scope”] extends “session” ? string : null; input: Schema.Schema.Type<C[K][“input”]> }>) => Effect.Effect<Schema.Schema.Type<C[K][“output”]>, ExtensionCommandError | ExtensionStateError | AdmissionError, ExtensionTx | ExtensionRead> }

Handlers for every declared command, run in one core transaction.

C extends CommandsV1


ProjectionSpecV1<P, PI> > = Readonly<{ schema: Schema.Schema<P, PI>>; }>

The encoded Session projection slot.

P

PI


ExtensionContractV1<C, P, PI> > = Readonly<{ id: ExtensionId; commands: C; projection: ProjectionSpecV1<P, PI> > | null; }>

Shared by the server definition and the client binding: one schema set.

C extends CommandsV1

P

PI


ExtensionContextV1<S, Config> > = Readonly<{ manifest: ExtensionManifestV1; revision: ExtensionRevision; grants: readonly ExtensionCapabilityV1[]; config: Config; state: StateHandleV1<S>>; admit: (input) => Effect.Effect<AdmitResultV1, AdmissionError, ExtensionTx>>; timers: TimersV1; observe: (sessionId) => Effect.Effect<void, never, ExtensionTx>>; transact: <A, E>>(body) => Effect.Effect<A, E | ExtensionStop>>; }>

Everything core hands one activated extension.

S extends CollectionsV1

Config extends Json


RunIdentityV1 = Readonly<{ sessionId: string; workId: string; origin: RunOriginV1 | null; }>

One run the extension may attach tools to.


AgentHooksV1<C, P> > = Readonly<{ onSettled?: (page) => Effect.Effect<void, HookError, ExtensionTx | ExtensionRead>>; onTimer?: (timer) => Effect.Effect<void, HookError, ExtensionTx | ExtensionRead>>; moduleForRun?: (run) => Effect.Effect<Option.Option<ModuleRegistration>>, HookError, ExtensionRead>>; project?: (sessionId) => Effect.Effect<Option.Option<P>>, HookError, ExtensionRead>>; compactionHandoff?: CompactionHandoffV1; commands: CommandHandlersV1<C>>; }>

Hooks returned by one activation; each runs in its own isolation boundary.

C extends CommandsV1

P


ShiitakeExtensionV1<S, C, P, PI, Config> > = Readonly<{ manifest: ExtensionManifestV1; contract: ExtensionContractV1<C, P, PI>>; config: Config; state: StateSpecV1<S>>; activate: (context) => Effect.Effect<AgentHooksV1<NoInfer<C>>, NoInfer<P>>>>, ExtensionActivationError, Scope.Scope>>; }>

One trusted v1 extension definition.

S extends CollectionsV1

C extends CommandsV1

P

PI

Config extends Json


RosteredExtensionV1 = Readonly<{ manifest: ExtensionManifestV1; }> & Brand.Brand<"RosteredExtensionV1">>

Opaque rostered value; core reads the definition existentially.


AnyAgentHooksV1 = Readonly<{ onSettled?: NonNullable<AgentHooksV1<CommandsV1, never>>["onSettled"]>; onTimer?: NonNullable<AgentHooksV1<CommandsV1, never>>["onTimer"]>; moduleForRun?: NonNullable<AgentHooksV1<CommandsV1, never>>["moduleForRun"]>; project?: (sessionId) => Effect.Effect<Option.Option<unknown>>, HookError, ExtensionRead>>; compactionHandoff?: CompactionHandoffV1; commands: Readonly<Record<string, (call) => Effect.Effect<unknown, ExtensionCommandError | ExtensionStateError | AdmissionError, ExtensionTx | ExtensionRead>>>>>>; }>

Existential hooks as stored by core; each handler is re-typed by its schema.


AnyExtensionDefinitionV1 = Readonly<{ manifest: ExtensionManifestV1; contract: Readonly<{ id: ExtensionId; commands: CommandsV1; projection: Readonly<{ schema: Schema.Schema.AnyNoContext; }> | null; }>; config: Json; state: StateSpecV1<CollectionsV1>>; activate: (context) => Effect.Effect<AnyAgentHooksV1, ExtensionActivationError, Scope.Scope>>; }>

Existential definition stored by core.


ExtensionRosterV1 = Readonly<{ roster: readonly RosteredExtensionV1[]; grants: readonly ExtensionGrantV1[]; permitRunTools?: (input) => boolean; }>

A host’s explicit roster and grants.


ExtensionStatusV1 = Readonly<{ id: string; version: string; revision: string; status: "running" | "stopped"; reason: string | null; stoppedAt: number | null; }>

Visible lifecycle of one rostered extension (wire representation).


ExtensionSlotV1 = Readonly<{ status: "running"; revision: string; value: Json; }> | Readonly<{ status: "stopped"; revision: string; reason: string; }>

One extension’s encoded slot in a Session snapshot or frame.


BorrowedCompactionViewV1 = Readonly<{ messagesToSummarize: readonly Message[]; turnPrefixMessages: readonly Message[]; result: Readonly<{ summary: string; firstKeptEntryId: string; tokensBefore: number; usage: Usage; details?: Readonly<{ readFiles: readonly string[]; modifiedFiles: readonly string[]; }>; }>; contextRevision: string; }>

Borrowed view of the exact objects already built for compaction.


CompactionHandoffResultV1 = Readonly<{ type: "accepted"; receipt: string; }> | Readonly<{ type: "discarded_by_policy"; }>

Result returned before a borrowed compaction view may be released.


CompactionHandoffErrorV1 = Readonly<{ _tag: "CompactionHandoffError"; message: string; }>

Typed failure returned by the compaction contribution.


CompactionHandoffV1 = Readonly<{ consume: (view) => Effect.Effect<CompactionHandoffResultV1, CompactionHandoffErrorV1>>; }>

The exclusive compaction handoff callable of one extension.

const ExtensionId: brand<filter<filter<filter<typeof String$>>>>>>, "ExtensionId">>

ASCII extension identity shared by the server definition and its clients.

defineExtension<S, C, P, PI, Config>>(definition): RosteredExtensionV1

Freeze one definition into an opaque roster entry.

S extends Readonly<Record<string, Readonly<{ schema: AnyNoContext; key: (value) => string; index?: (value) => string | null; }>>>

C extends Readonly<Record<string, Readonly<{ scope: "session" | "agent"; input: AnyNoContext; output: AnyNoContext; }>>>

P

PI

Config extends Json

ShiitakeExtensionV1<S, C, P, PI, Config>

RosteredExtensionV1


defineCollection<A, I>>(schema, keys): CollectionSpecV1<A, I>>

Declare one collection with its key inferred from the row schema.

A

I

Schema<A, I>

Readonly<{ key: (value) => string; index?: (value) => string | null; }>

CollectionSpecV1<A, I>