Skip to content
Concepts

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.

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.

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.

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.

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.

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.

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.

@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.