## Package Lab\:&#32;one file to an embedded host

Theo maintains fictional field observations\.&#32;His first notebook fits in one file\.&#32;Later he wants reusable helpers\,&#32;optional package features and an application that embeds OMP\.

His obstacle is architectural\:&#32;an imported helper\,&#32;an extension entry and an installed plugin are not the same thing\.

[Package Lab’s complete instructions](<https://present-sketch-tp94.here.now/examples/package-lab/README.md>)&#32;accompany the&#32;[source bundle](<https://present-sketch-tp94.here.now/downloads/extensions-examples.zip>)\.

### Milestone\:&#32;one file is enough

Files\:

- [Single\-file entry](<https://present-sketch-tp94.here.now/examples/package-lab/single/field-notes.ts>)
- [Package Lab root manifest](<https://present-sketch-tp94.here.now/examples/package-lab/package.json>)

**Terminal shell\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/single/field-notes.ts"
~~~

**Human OMP slash command\:**

~~~text
/field-notes reed
~~~

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

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

~~~json
{"op":"inspect","value":"reed"}
~~~

~~~json
{"op":"query","value":"marsh"}
~~~

**Observed discovery details\:**

~~~json
{
  "operations": ["discover", "inspect", "query"],
  "ids": ["reed", "fern"],
  "writes": false
}
~~~

Inspection returns the fictional reed beside the footbridge\.

The tool lowercases and trims its search value\.&#32;Query searches ID\,&#32;habitat and text\.&#32;The slash command is narrower\:&#32;it lists all notes or matches an exact lowercased ID\.

Unknown tool inspection IDs throw\.&#32;Unknown human IDs produce a warning\.

This entry does not declare&#32;`approval`&#32;or&#32;`loadMode`\.&#32;Therefore the current defaults apply\:&#32;an execution\-tier approval declaration and discoverable presentation\,&#32;despite the domain being read\-only\.&#32;Descriptions do not set approval policy\.

The command uses notifications without a headless message fallback\.&#32;Its machine tool remains useful without the notification\.

**Exercise\:**&#32;query&#32;`woodland`\.

**Checkpoint\:**&#32;the tool finds&#32;`fern`\.&#32;Do not assume&#32;`/field-notes woodland`&#32;performs the same full\-text query\;&#32;it is an ID\-oriented human command\.

### Milestone\:&#32;helpers become ordinary imports

Theo adds a current selection\.&#32;He wants both&#32;`/notebook fern`&#32;and a tool act to change the same selection\,&#32;without duplicating lookup logic\.

Files\:

- [Multi\-file entry](<https://present-sketch-tp94.here.now/examples/package-lab/multi/index.ts>)
- [Notebook helper](<https://present-sketch-tp94.here.now/examples/package-lab/multi/notebook.ts>)

**Terminal shell—after exiting the single\-file launch\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/multi"
~~~

The directory resolves to&#32;`index.ts`\.&#32;`notebook.ts`&#32;is imported as a helper\;&#32;it is not independently bound as another extension\.

**Complete helper source—[`multi/notebook.ts`](<https://present-sketch-tp94.here.now/examples/package-lab/multi/notebook.ts>)\:**

~~~ts
export const notes = [
    { id: "reed", text: "Fictional reeds border the marsh trail." },
    { id: "fern", text: "Fictional ferns shade the woodland path." },
] as const;

export function inspectNote(id: string): (typeof notes)[number] {
    const note = notes.find(candidate => candidate.id === id);
    if (!note) throw new Error(`Unknown note id: ${id}. Discover ids before selecting.`);
    return note;
}
~~~

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

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

~~~json
{"op":"inspect","id":"reed"}
~~~

~~~json
{"op":"act","id":"reed"}
~~~

~~~json
{"op":"query"}
~~~

**Human OMP slash commands\:**

~~~text
/notebook fern
/notebook
~~~

**Observed\:**&#32;command and tool shared the factory’s state\.&#32;An invalid action left the earlier selection intact\.&#32;Exact completed arguments returned no completion\.

#### The lifetime correction

The selection is declared inside the factory\:

**Exact excerpt from&#32;[multi\/index\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/multi/index.ts>)\:**

~~~ts
// Factory-local state belongs to one binding, not Bun's cached module.
let selected: string | null = null;
~~~

That prevents this variable from being shared merely because a cached module is rebound\.&#32;It does&#32;**not**&#32;make it transcript\-local\.

**Observed SDK lifecycle checks\:**

| Operation | Transcript identity | Factory selection |
| --- | --- | --- |
| Select&#32;`reed` | Unchanged | `reed` |
| `newSession()` | Changes | Still&#32;`reed` |
| Select&#32;`fern`\,&#32;then&#32;`switchSession()` | Changes to a real target header | Still&#32;`fern` |
| Construct a separate SDK host | Separate binding | Starts at&#32;`null` |
| Rebind prepared factories | Fresh runtime and extension objects | Starts at&#32;`null` |

The corrected teaching language is&#32;**factory\-local**&#32;or&#32;**binding\-local**\,&#32;not session\-local\.

The public helpers deliberately do not reset on&#32;`session_switch`&#32;and do not append selection entries\.

**Exercise\:**&#32;select&#32;`fern`\,&#32;use&#32;`/new`\,&#32;then run&#32;`/notebook`\.

**Checkpoint\:**&#32;within the reused binding\,&#32;it remains selected\.&#32;If you want transcript\-local selection\,&#32;add explicit lifecycle reset or branch reconstruction in a new exercise\;&#32;do not claim the supplied stage already does that\.

### Milestone\:&#32;a manifest declares several entries

Theo now wants a catalog and a reusable prompt\.&#32;An optional summary tool should be available only when selected as a package feature\.

Files\:

- [Manifest](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/package.json>)
- [Catalog entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/catalog.ts>)
- [Resource entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/resources.ts>)
- [Optional summary entry](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/entries/summary.ts>)
- [Prompt file](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/prompts/field-observation.md>)

**Exact config file—[`manifest/package.json`](<https://present-sketch-tp94.here.now/examples/package-lab/manifest/package.json>)\:**

~~~json
{
  "name": "omp-workbook-field-notes",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "description": "Fictional field notes with an optional plugin summary",
  "omp": {
    "extensions": ["./entries/catalog.ts", "./entries/resources.ts"],
    "features": {
      "summary": {
        "description": "Add the fictional habitat summary tool",
        "default": false,
        "extensions": ["./entries/summary.ts"]
      }
    }
  }
}
~~~

**Terminal shell—load the base manifest entries\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/manifest"
~~~

The base catalog registers&#32;`field_catalog`&#32;and&#32;`/field-catalog`\.

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

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

~~~json
{"op":"query","habitat":"marsh"}
~~~

Explicit&#32;`-e`&#32;directory loading reads the base&#32;`extensions`&#32;list\.&#32;It does&#32;**not**&#32;select plugin features\.

To try the optional entry without installation\:

**Terminal shell\:**

~~~sh
omp --no-extensions -e "$EXAMPLES/package-lab/manifest" -e "$EXAMPLES/package-lab/manifest/entries/summary.ts"
~~~

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

~~~json
{}
~~~

**Expected result details\:**

~~~json
{"fictional":true,"habitats":["marsh","woodland"]}
~~~

`field_summary`&#32;has no&#32;`op`&#32;parameter\.

**Observed\:**&#32;default installed\-plugin feature resolution selected two entries\;&#32;enabling&#32;`summary`&#32;selected three\.&#32;Explicit base\-manifest directory discovery did not include the optional entry\.

Resource registration is explained in&#32;[Resources\,&#32;event buses\,&#32;MCP and Gemini manifests](<https://present-sketch-tp94.here.now/chapters/extensions-resources-event-buses-mcp-and-gemini-manifests#extensions-resources-event-buses-mcp-and-gemini-manifests>)\.&#32;Its runner\-level proof does not establish automatic prompt\-menu rendering\.

**Exercise\:**&#32;why not import&#32;`summary.ts`&#32;for its side effects from the base catalog\?

**Answer\:**&#32;that would undermine the feature\-selection boundary\.&#32;A feature is meaningful only if its code is not activated through another unconditional path\.

### Milestone\:&#32;embed a factory in an SDK host

Theo’s final goal is to include the notebook in another application without installing a global plugin\.

Files\:

- [Inline factory creator](<https://present-sketch-tp94.here.now/examples/package-lab/inline/factory.ts>)
- [Session construction](<https://present-sketch-tp94.here.now/examples/package-lab/inline/session.ts>)
- [Runnable host script](<https://present-sketch-tp94.here.now/examples/package-lab/inline/run.ts>)

`createFieldNotesExtension(name)`&#32;returns an&#32;`ExtensionFactory`\.&#32;The SDK’s&#32;`extensions`&#32;option takes&#32;**functions**\,&#32;not file paths\.&#32;File paths belong in&#32;`additionalExtensionPaths`\.

**Exact excerpt—session options in&#32;[inline\/session\.ts](<https://present-sketch-tp94.here.now/examples/package-lab/inline/session.ts>)\;&#32;use the linked complete module\:**

~~~ts
const result = await createAgentSession({
    cwd: scratchDirectory,
    agentDir: path.join(scratchDirectory, "agent"),
    authStorage: auth,
    modelRegistry: new ModelRegistry(auth, path.join(scratchDirectory, "models.yml")),
    settings: Settings.isolated(),
    sessionManager: SessionManager.inMemory(scratchDirectory),
    disableExtensionDiscovery: true,
    extensions: [createFieldNotesExtension("Fictional field notebook")],
    enableMCP: false,
    enableLsp: false,
    skipPythonPreflight: true,
    preloadedCustomToolPaths: [],
    skills: [], rules: [], contextFiles: [], promptTemplates: [], slashCommands: [],
    toolNames: [],
});
~~~

The complete module constructs an in\-memory auth store\,&#32;closes auth on failure\,&#32;and returns a&#32;`close()`&#32;function that disposes the session before closing auth\.

Install the&#32;**workbook\-compatible**&#32;SDK package into the environment that runs the script\.&#32;Package Lab marks it as an optional peer\;&#32;the example is private and is not presented as a registry\-published package\.

**Terminal shell—with that runtime dependency available\:**

~~~sh
bun "$EXAMPLES/package-lab/inline/run.ts"
~~~

The script prints metadata containing&#32;`providerPromptSent: false`\,&#32;the&#32;`inline_notebook`&#32;tool and the registered command names\,&#32;then disposes the host\.&#32;It never calls&#32;`session.prompt()`\.

That is not a blanket no\-network guarantee for arbitrary host startup\:&#32;model registries and other host configuration can perform discovery\.&#32;The recorded isolated run had network denied\.

#### Binding is not initialization

`createAgentSession()`&#32;builds the session and binds factories\.&#32;A mode adapter subsequently initializes the extension runner’s live actions\/UI\.

The public metadata script does not pretend to be a full TUI\/RPC host\.&#32;Its notebook tool uses closure state\,&#32;so the proof could exercise it through the real adapter without needing message actions\.&#32;An embedding that uses&#32;`appendEntry()`\,&#32;message delivery\,&#32;dialogs or session actions must wire the real runtime—not replace those methods with successful no\-ops\.

The inline tool’s&#32;`discover`\,&#32;`inspect`&#32;and&#32;`query`&#32;all return its notebook contract and current selection\.&#32;It does not implement a separate per\-note inspection record\.&#32;`act`&#32;requires&#32;`reed`&#32;or&#32;`fern`\.

#### Prepared factories versus bound instances

Prepared factories can be rebound to a fresh runtime\.&#32;Already\-bound extension instances close over their original API and must not be forwarded to an independently constructed SDK session\.

The CLI’s early preload is a special same\-owner optimization for flag parsing\.&#32;It is not a general pattern for sharing a parent’s loaded instances with a child\.

**Exercise\:**&#32;share a module\-scope mutable selection between two SDK hosts\.&#32;What risk have you introduced\?

**Answer\:**&#32;the module may be cached\,&#32;so both factory bindings can reach the same mutable variable\.&#32;Put binding state inside the factory\,&#32;and use explicit storage for any state intentionally shared across bindings\.

*Source\,&#32;snapshot 2026\-08\-29\:&#32;linked Package Lab files\;&#32;`packages/coding-agent/src/sdk.ts`\,&#32;`CreateAgentSessionOptions`\,&#32;`createAgentSessionScoped`\;&#32;`packages/coding-agent/src/extensibility/extensions/loader.ts`\,&#32;`loadExtensions`\,&#32;`bindPreparedExtensions`\;&#32;recorded loading\/rebinding scenarios\.*
