Concepts
Botanical's product Shiitake extensions: goal, loop and Almanac on the generic extension surface
@fungi.computer/shiitake-extensions supplies Fungi’s questions, goals, loops,
plans, Almanac schedules, skills and Home. Hub and Local choose the shared
roster and explicitly grant its capabilities. Shiitake core supplies the generic
extension API.
Core concepts
Section titled “Core concepts”| Extension | Ownership and behavior |
|---|---|
| Ask | Up to four open decisions per Session. The model asks or withdraws; humans answer or decline. A live waiter receives the answer, while a parked question resumes through one follow-up. On Local, an Ask raised during work another Agent sent waits on that sender first: it always parks, reads waiting on <name> instead of needing a person, and rides the child’s reply to the sender as waiting_on_you. Only that sender’s Session can answer it with agents.answer, or pass it to a person with agents.escalate; a person may always answer. An Ask the sender neither answers nor escalates goes to a person when the sender’s turn ends, or after fifteen minutes. Hub sends every Ask to a person. |
| Goal | One live or paused objective per Session. The model creates, reads and completes it through goal.* tools, from any later turn of its Session; humans set, pause, resume or clear it. Esc, failed work, a block or three tool-less continuations pause it; a tool-less goal turn while a sub-agent command is live waits instead. Closing the app pauses it only if work stops without resuming; background work and runtime-auto-resumed turns keep it live. An achieved or cleared goal becomes one chat line in its history. |
| Loop | One repeated prompt per Session. It runs on a fixed cron cadence or lets its own active run request the next occurrence. Commands start, pause, resume and stop it. |
| Plan | One checklist per Session. plan.update_plan replaces it whole, with at most one step in progress and at most 50 steps; steps: [] clears it. The human clears it with /plan clear or the footer row’s Clear plan action, through the same owner. /plan opens the full checklist in the footer; updates keep only the current step and progress visible, and a finished or cleared plan leaves the footer while its history stays in state. |
| Almanac | Agent-scope one-shot or recurring schedules. Each schedule targets a Session and tracks admission, retry and its next due time. |
| Skills | Host-supplied, read-only skill discovery. Metadata enters the prompt; skill.load reads a skill body or supporting file; /skills lists them. |
| Home | Agent-scope choice of an attached Computer. Each run captures its scoped file tools and current attachment choice. |
| Subagents | A footer view: one row per Agent this Session sent work to or created, with its live status (working, waiting on an Agent, needs you, idle, failed, unavailable). A row opens that Agent’s conversation, a question that needs a person can be answered from its row, and /agents collapses the rows to one summary row and expands them again. It owns no relationship and no durable state. |
An active Goal is quiet system context on every run. Continuation instructions apply only to runs admitted by Goal with its continuation correlation; human messages receive the Goal context without that directive. Both sections are bounded by UTF-8 bytes. Goal turns stay out of the transcript; the footer shows only a live goal (“Pursuing goal since 14:02”) or a paused one (“Goal paused (/goal resume)”), and each finished goal adds one chat line after its last work. Other extensions keep their own display policy.
Known limitation: Session forks copy transcript entries without their original command-admission rows. Inherited Goal turns therefore lose the extension-origin metadata used for compact notes and appear as ordinary message bubbles.
Goal pause stops continuation, not already accepted work. Model-requested
completion or blocking is provisional until successful settlement; a blocked
goal pauses and resumes like any paused goal. Each run’s goal tools are fenced
by the goal revision that run saw, so a later follow-up command (for example a
parent’s message to a sub-agent) can complete, block, clear or resume a goal
that paused on its own; a goal a person paused or stopped accepts only
completion from the model, and only a person clears a live goal. When a
tool-less goal turn ends while a recorded sub-agent command is live, the goal
raises goal-waiting attention and rechecks on a timer instead of counting a
stall; it continues once the child settles or replies. Only a live or paused
goal asks before a replacement, which uses the observed revision to avoid
replacing a newer goal; the replaced goal is recorded as cleared. When a runtime
restart (a Hub deploy or eviction, a Local service restart) interrupts a goal
turn, the turn resumes and the goal stays live. A live goal pauses when any turn
of its Session fails, cannot resume or is stopped by a person (Esc), including
the person’s own messages (“The last turn failed”). A missing extension origin
does not establish a human sender; mail and host admissions can also lack one.
Goal’s shared wire and durable row use pauseCause: { kind, detail } while
paused and null while active. The closed kinds are person, blocked,
stalled, cancelled, failed, and no-terminal; display detail is nullable
text, never a discriminator. Only blocked/stalled goals emit goal-blocked or
goal-stalled attention, kept out of the main inbox with inbox: false.
Questions and these two Goal kinds ask for a person in Herdr activity;
person-paused, cancelled and failed goals do not. State version 6 discards
disposable preview Goal rows from versions 4 and 5 rather than reading the
superseded representation.
Loop tools are offered in every run. loop.get, loop.start and loop.stop
work from any run of the Session, and the model may start a loop itself.
loop.schedule_next is accepted only in the active self-paced loop’s own run;
omitting it ends that loop. A fixed cadence uses the core timer and expires
seven days after it starts. Almanac retries a failed admission per schedule, so
one refused occurrence does not become another schedule’s failure. Use the
recorded error and the owner’s resume command when recovering.
The human /goal, /loop and /plan flows are advertised as
whistle.catalog/v1 catalogs. A compatible client renders the same server-owned
interactions. A human command catalog, a per-run tool module and model tool
discovery are separate contracts. Inspect the host’s exposed tool catalog before
relying on a model-facing operation.
State and lifetime
Section titled “State and lifetime”Question
Section titled “Question”question.ask creates one to four decision questions. Its namespace is
question, and its extension identity is botanical-session-question. Inspect
forage.describe({path: "question.ask"}).inputSchema for the validated bounds,
including the 12-character question header. A waiting call receives the human’s
answer in-turn; mode: "continue" parks it and returns its askId. The live
wait leaves sixty seconds of the actual execute deadline and is capped at ten
minutes. Pending steering also parks the question so Mule can deliver the steer.
Each Session holds at most four open Asks, with one live Ask per run.
Bind questionContract from /question with bindSessionExtension to read its
decoded projection and call list, answer or decline. Human responses
include an askId and a stable requestId. Reuse that request identity after
an uncertain response: an exact retry returns its original receipt, while a
changed payload conflicts. Answer every question with offered labels or nonempty
free text. The model has no answer operation.
The /answer catalog conversation selects a waiting Ask and collects an answer
for each question. Multiple selections use comma-separated labels; quote a label
that contains a comma. Every choice also permits free text.
Optional onExpire specifies afterMinutes (5 through 10,080) and an action:
stop explicitly tells the model not to proceed; proceed_with_recommended
selects the first option of each question. A parked answer or expiry admits one
self-contained follow-up. The Ask owner clears attention when it closes, and the
live waiter and durable timer share the same expiry transition. Restart recovery
reconciles committed answers before allowing another delivery. An in-turn answer
retains its delivery receipt until Mule saves a successful tool outcome. If a
crash loses that outcome and no re-ask receives it, settlement sends the answer
once as a follow-up. A cancelled run suppresses that redelivery.
Client cards, goal/loop attention policy and external notifications are separate contributions; the Ask owner alone does not provide those surfaces.
Owner transactions
Section titled “Owner transactions”Collections are namespaced to the extension. Schema, version, migrations and configuration contribute to its revision. Settled hooks, timer hooks and command handlers commit through core transactions. They must finish synchronously and must not perform external I/O.
Extensions send prompts through the accepted-command door and wake through core timers. An accepted prompt is queued work, not a finished model reply. Core owns replay cursors and publication after commit. A stopped extension exposes its reason; the host can resume it without inventing a separate background loop.
The host creates homeExtension({ computers }), where computers.read()
synchronously returns the current attachments as { name, attachmentId, port }.
Each port is already scoped to that attachment by the host. Home never creates
credentials or bypasses the host gate. Add the returned extension and its
exported homeGrant (state, files, prompt, projection, commands).
Home is composed by the host, not included in botanicalAgentExtensions. The
Hub and demo add it with their own Computer ports and grants.
Home records one Agent-scope attachment choice. With no recorded choice, a run
adopts a sole attachment once. Zero or multiple attachments require a person to
choose /home <name>. Removing the chosen attachment keeps the choice and
offers no direct file tools until a person chooses again. Reordering attachments
never changes Home. The Session command changes Agent state, so other Sessions
see that choice on their next run.
Each run captures its table, Home and prompt once. The tools are Knapsack’s
branded read, list, glob, and grep, with apply_patch only when Home’s
port has a native patch engine. Bare paths and computer://home/ use Home;
computer://<name>/ uses another captured attachment. Patches always use Home’s
native engine and path jail. Returned file identity stays owned by the scoped
port. There is no Home model tool and no shared Files module.
The Agent-scope seed command is an unadvertised host migration operation. It
records an old attachment id only when no choice exists, including when that
attachment is detached; subsequent seed calls cannot overwrite a human choice.
Hosts keep the control door human/operator-authorized.
Home tests exercise selection, capture, detachment, routing
and gating on real Shiitake.
TypeScript consumer dependencies
Section titled “TypeScript consumer dependencies”Install @modelcontextprotocol/sdk@1.25.2 alongside this package when compiling
its complete public declarations. The shared provider SDK imports those types
through an optional peer; the packed consumer supplies it explicitly. This does
not require a provider account or make a provider call.
Subagents
Section titled “Subagents”The host creates subagentsExtension({ source }) and adds subagentsGrant
(projection and commands: the human /agents command only collapses or
expands the footer rows, per Session and in memory).
source.read(sessionId, limit) returns the Agents that Session sent work to or
created, newest first, with a count of the rest; source.open is the host’s
existing Agent access. Each child’s status is its owner’s own Session state:
running is working, done is idle and errored is failed. Attention that
waits on a person (a question, or an approval an extension raises) is
needs you; a question that waits on the Agent that sent the work is
waiting on <name>, calm until it is escalated, and counted apart in the
collapsed summary, which is amber only when a child needs a person. A
needs you question carries its text and one Answer action that runs the
question’s own answer conversation. A child whose state cannot be read is
unavailable, never idle.
Rows refresh when a watcher reads the parent Session and they are at least a
second old. While a row is working or waiting, they poll every five seconds
until nobody has read the parent for thirty seconds. Like Home, the extension is
composed by the host, not included in botanicalAgentExtensions.
Packed consumer readiness
Section titled “Packed consumer readiness”Run pnpm run build, pnpm run pack and pnpm run stranger in this package.
The maintained stranger proof installs only package tarballs into a fresh npm
consumer, checks the exact shipped files, runs the public runtime fixture and
compiles every JavaScript entrypoint with strict NodeNext and no library-check
skip. This qualification does not publish npm releases or push a repository
split.
See LICENSE for the license and the package manifest for its public repository destination.
Conversation Agent composition
Section titled “Conversation Agent composition”@fungi.computer/shiitake-extensions/roster/conversation-agents supplies the
shared Local, Hub and Oyster conversation-Agent composition. The host supplies
its authorized Agent directory and SQLite owner; the roster records accepted
creates and sends for a read-only footer without granting relationships or
authority. Storage is prepared lazily. The host remains responsible for creation
admission, Agent access and resource lifetime.