## Seed Desk\:&#32;welcome and inventory

Mara’s first goal is modest\:&#32;stop repeating the fictional seed desk’s opening hours\.&#32;Her obstacle is that a friendly human command alone would leave the agent without a supported way to retrieve the same information\.

She chooses two entrances to one small domain\:&#32;a human command and a read\-only tool\.

### Milestone\:&#32;a welcoming desk for both readers

**Starting state\:**&#32;no reservation state\,&#32;no inventory mutation and no external service\.

Files\:

- [Stage 1 entry](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/index.ts>)
- [Tool description](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/tool.txt>)
- [Seed Desk instructions](<https://present-sketch-tp94.here.now/examples/seed-desk/README.md>)

**Terminal shell—launch stage 1\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/01-welcome/index.ts"
~~~

**Human OMP slash commands—enter in the composer\:**

~~~text
/seeds welcome
/seeds hours
~~~

The hours response is\:

**Expected local output\:**

~~~text
Our fictional desk opens Saturday, 10:00-12:00. No booking has been made.
~~~

The machine interface is&#32;`seed_welcome`\.

**Model tool arguments—call&#32;`seed_welcome`\:**

~~~json
{"op":"discover"}
~~~

**Model tool arguments—call&#32;`seed_welcome`\:**

~~~json
{"op":"inspect","topic":"hours"}
~~~

The first operation advertises&#32;`discover`&#32;and&#32;`inspect`\,&#32;the topics\,&#32;scope and quiet flag in structured&#32;`details`\.&#32;The second returns the same hours text used by the slash command\.

Important distinction\:&#32;`details`&#32;is useful to SDK callers\,&#32;hosts and renderers\.&#32;It is not automatically model\-visible\.&#32;This stage’s ordinary text content tells the model which topics it can inspect\;&#32;the quiet flag is in&#32;`details`\,&#32;not duplicated into that text\.

#### What the code changes

The async factory reads its description from a real adjacent file\.&#32;It registers\:

- `seed_welcome`\,&#32;with&#32;`approval: "read"`&#32;and&#32;`loadMode: "essential"`\;
- the boolean flag&#32;`seed-quiet`\;
- a&#32;`session_start`&#32;notification\;
- `/seeds`\,&#32;including argument completion\.

It does&#32;**not**&#32;register a reservation operation\.

**Exact excerpt—completion behavior in&#32;[stage 1’s entry](<https://present-sketch-tp94.here.now/examples/seed-desk/01-welcome/index.ts>)\;&#32;not a standalone replacement file\:**

~~~ts
getArgumentCompletions(prefix) {
    const matches = choices.filter(value => value.startsWith(prefix.trimStart()));
    // A completed sole match MUST release Enter to submit the command.
    if (matches.length === 1 && matches[0] === prefix.trim()) return null;
    return matches.length ? matches.map(value => ({ value: `${value} `, label: value })) : null;
},
~~~

Mara types&#32;`/seeds wel`\.&#32;The completion offers the full argument text&#32;`welcome `\.

When&#32;`welcome`&#32;is already the sole exact match\,&#32;returning&#32;`null`&#32;lets Enter submit rather than continually reaccepting the completion\.&#32;A completion\-array equality test is helpful\,&#32;but it is not a terminal\-key\-dispatch test\.

#### Inspect progress

**Observed\:**

- `wel`&#32;produced&#32;`welcome `\.
- `welcome`&#32;and&#32;`welcome `&#32;produced no completion\.
- The tool’s greeting matched the headless command’s greeting\.
- Quiet startup produced zero startup notices\.
- A headless command produced one custom message\.

The supplied Seed Desk proof simulated confirmation\/notification UI\.&#32;It did not exercise actual terminal autocomplete and Enter behavior\.

#### Quiet is a startup choice\,&#32;not a permission

Exit the stage and restart it with the flag\.

**Terminal shell—suppress the startup notice\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/01-welcome/index.ts" --seed-quiet
~~~

This suppresses only the startup notification\.&#32;Commands and tools still work\.

Register flag names without the leading&#32;`--`\;&#32;use the leading&#32;`--`&#32;in the shell\.&#32;In this parser\,&#32;a boolean flag’s presence means&#32;`true`\.&#32;Do not assume&#32;`--seed-quiet=false`&#32;means false\;&#32;omit the flag to use its false default\.

#### Failure and mode boundaries

- An inspect request without a topic throws an error\.
- An unknown human verb produces guidance and changes nothing\.
- The tool checks its&#32;`AbortSignal`&#32;before doing work\.
- With UI\,&#32;the command uses&#32;`notify()`\.
- Without UI\,&#32;it uses&#32;`sendMessage(..., { triggerTurn: false })`\,&#32;so the answer is not lost in an inert notification\.
- Reloading code is not accomplished by directly running the TypeScript file\.&#32;Restart the explicit launch when testing an edit\.

**Exercise\:**&#32;ask the agent to reserve basil through&#32;`seed_welcome`\.

**Checkpoint\:**&#32;it should report that no stock\-changing operation exists\,&#32;not invent&#32;`reserve`&#32;or attempt to invoke&#32;`/seeds`\.

### Milestone\:&#32;the desk gains a real query surface

The next Saturday\,&#32;Mara’s fictional desk has three seed varieties\.&#32;Repeating a prose list has become awkward\:&#32;the agent needs stable IDs\,&#32;family filters and a clear error for an invented ID\.

She replaces stage 1 with stage 2\.&#32;These stages are independent\,&#32;not cumulative imports\.

Files\:

- [Stage 2 entry](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/index.ts>)
- [Shared catalog functions](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/catalog.ts>)
- [Fictional inventory](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/inventory.json>)
- [Tool description](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/tool.txt>)

**Terminal shell—after exiting stage 1\:**

~~~sh
omp --no-extensions --no-skills -e "$EXAMPLES/seed-desk/02-catalog/index.ts"
~~~

**Human OMP slash commands\:**

~~~text
/seeds query
/seeds query herb
/seeds inspect basil-genovese
~~~

**Model tool arguments—call&#32;`seed_catalog`\:**

~~~json
{"op":"query","family":"herb"}
~~~

**Model tool arguments—call&#32;`seed_catalog`\:**

~~~json
{"op":"inspect","id":"basil-genovese"}
~~~

Stage 2 supports&#32;**`query`&#32;and&#32;`inspect`&#32;only**\.&#32;It does not have a&#32;`discover`&#32;operation merely because stage 1 had one\.

#### One shared domain\,&#32;two entrances

This is the complete small helper used by both interfaces\.

**TypeScript source—[stage 2’s&#32;`catalog.ts`](<https://present-sketch-tp94.here.now/examples/seed-desk/02-catalog/catalog.ts>)\:**

~~~ts
import fixture from "./inventory.json";

export interface Seed {
    id: string;
    name: string;
    family: string;
    packets: number;
}

export const inventory: readonly Seed[] = fixture;

export function inspect(id: string): Seed {
    const seed = inventory.find(item => item.id === id);
    if (!seed) throw new Error("Unknown seed ID. Query the catalog for valid IDs. Nothing changed.");
    return seed;
}

export function query(family?: string): readonly Seed[] {
    return family ? inventory.filter(seed => seed.family === family) : inventory;
}

export function summary(seeds: readonly Seed[]): string {
    return seeds.length
        ? seeds.map(seed => `${seed.id}: ${seed.name}, ${seed.packets} fictional packets (${seed.family})`).join("\n")
        : "No fictional seeds match that family.";
}
~~~

**Expected output for the herb query\:**

~~~text
basil-genovese: Genovese basil, 8 fictional packets (herb)
~~~

The tool returns that summary in&#32;`content`\,&#32;and records in\:

- `details.scope: "fictional-fixture"`
- `details.seeds`

This is better than asking the agent to scrape a notification\.&#32;The tool gives it stable targets\,&#32;while the host gets structured records\.

#### Inspect progress and failure

**Observed\:**&#32;the real loader\,&#32;runner and intercepted tool adapter returned only&#32;`basil-genovese`&#32;for the herb query\.&#32;Slash\/tool summaries matched\.&#32;Unknown inspection IDs threw\.&#32;A pre\-aborted call was refused through the intercepted path\.

The family match is exact and case\-sensitive\.&#32;An unknown family gives an empty query result\;&#32;an unknown inspect ID is an error\.

No read appends reservation state\.

**Exercise\:**&#32;compare these two requests\:

**Model tool arguments—call&#32;`seed_catalog`\,&#32;one request at a time\:**

~~~json
{"op":"query","family":"unknown-family"}
~~~

~~~json
{"op":"inspect","id":"unknown-seed"}
~~~

**Answer\:**&#32;the query reports no matches\.&#32;The inspection fails because it promises to identify one existing target\.

### What changes next\?

Mara can now ask\,&#32;“Which herbs are available\?” But the eight packets are still a static fixture\.&#32;To represent a reservation\,&#32;she needs a state model\,&#32;an ownership scope and a concurrency check—not merely another button\.

That is the next chapter\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;the linked Seed Desk files\;&#32;`packages/coding-agent/src/extensibility/extensions/types.ts`\,&#32;`ToolDefinition`\;&#32;`packages/coding-agent/src/extensibility/extensions/wrapper.ts`\,&#32;`RegisteredToolAdapter`\,&#32;`ExtensionToolWrapper`\.*
