Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A Claude Code plugin for creating, building, testing, and deploying [AppOS](http
v3.0 re-anchors every teaching surface on the published SDK 3.0.0 (`@appos.space/plugin-types@3.0.0`), the surface the shipped AppOS 1.0.0 host actually exposes. The plugin's own version is deliberately aligned with the SDK major it teaches (it is NOT a `minHostVersion` — that stays `"1.0.0"`). Key changes:

- **Full 3.0.0 API surface** — 43 namespaces on `PluginContext` (of which 21 core-plugin namespaces: actions, palette, scheduler, vault, store, resources, tokens, bundles, entities, fields, ledger, views, surfaces, protocols, notifications, input, webhook, llm, recipes, sequences, fileSystem) and the 135-scope canonical permission model with 5 legacy aliases (deprecated).
- **`extensions[]` manifests** — manifest-declarative contributions to core-plugin extension points, including the required `actions.definition` dual-registration pattern: today the manifest entry is catalog/manifest metadata only (host bug fn-163), and the runtime `ctx.actions.register()` call is what provides discovery and execution — see `plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md`. <!-- remove when fn-163 lands -->
- **`extensions[]` manifests** — manifest-declarative contributions to core-plugin extension points, including the required `actions.definition` dual-registration pattern: the manifest entry is replayed into discovery at cold start and on plugin activation (visible in `ctx.actions.all()` / palette, badged "manifest only"), and the runtime `ctx.actions.register()` call is what binds the executable handler — see `plugins/appos-dev/skills/appos-plugin-dev/reference/extension-api.md`.
- **Scaffold pins `^3.0.0`** — `new-plugin` scaffolds depend on the 3.x SDK line; the SDK main entry ships no ambient globals, so all types are imported from the packages (3.0.1+ adds one opt-in globals subpath typing the host-injected `URL`; scaffolds declare that surface locally in the `src/jsc-globals.ts` `declare global` module instead — a `.ts` module, so it stays type-checked even under the scaffold's `skipLibCheck: true`).
- **Byte-verbatim type mirror + drift gate** — the bundled d.ts reference is a generated mirror of the published npm tarball, and CI type-checks every fenced code example against it (see "Knowledge verification" below).

Expand Down
2 changes: 1 addition & 1 deletion plugins/appos-dev/agents/plugin-architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ When the user describes what they want to build, map their requirements to speci
- `interPluginEvents` — Pub/sub between plugins

**Actions & automation (core-plugin tier)**
- `actions` — Public Action Fabric: typed, schema-validated, policy-bearing public actions (`register`, `invoke`, `all`, `registerFromCommand`). Declare actions in the manifest `extensions[]` (`actions.definition`) too, but ALWAYS pair with the runtime registration — manifest-declared actions don't reach discovery on their own yet (host bug fn-163; see `skills/appos-plugin-dev/reference/extension-api.md`): today the manifest entry is catalog/manifest metadata, and the runtime `ctx.actions.register(...)` call is what makes the action discoverable and executable. <!-- remove when fn-163 lands -->
- `actions` — Public Action Fabric: typed, schema-validated, policy-bearing public actions (`register`, `invoke`, `all`, `registerFromCommand`). Declare actions in the manifest `extensions[]` (`actions.definition`) too, but ALWAYS pair with the runtime registration — the manifest entry is replayed into discovery at cold start and on plugin activation (visible in `ctx.actions.all()` / `palette.query()`, badged "manifest only"; see `skills/appos-plugin-dev/reference/extension-api.md`), and the runtime `ctx.actions.register(...)` call is what makes the action executable (`ACTION_NOT_FOUND` on invoke until it binds).
- `palette` — Command palette integration for public actions (`query`, `history`, `pin`)
- `scheduler` — Job scheduling engine: interval/cron/notification/fsEvents/calendar/power/network triggers, conditions, run history
- `recipes` / `sequences` — Author-declared multi-step plans (linear or LLM-agent) dispatched through the action fabric
Expand Down
4 changes: 2 additions & 2 deletions plugins/appos-dev/commands/new-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,9 @@ If the plugin exposes public actions (command palette, automation), add an `exte
]
```

**Dual registration is required.** Manifest-declared actions don't reach discovery on their own yet (host bug fn-163; see `skills/appos-plugin-dev/reference/extension-api.md`): an `actions.definition` contribution alone currently never becomes palette-visible or invokable — no cold-start palette entry, no `ctx.actions.all()` stub, no Settings → Actions row. Today the manifest entry is catalog/manifest metadata (visible in catalogs and manifest scans), not runtime discovery. Pair EVERY `actions.definition` contribution with a runtime `ctx.actions.register(...)` or `ctx.actions.registerFromCommand(...)` call in `activate()` using the same id — the runtime registration is what makes the action discoverable and executable. Ship BOTH, exactly as `appos-plugin-ytdlp` does. <!-- remove when fn-163 lands -->
**Dual registration is required.** An `actions.definition` contribution is discoverable metadata (see `skills/appos-plugin-dev/reference/extension-api.md`): the host replays it into the discovery catalog at cold start and on plugin activation, so it surfaces as a `ctx.actions.all()` stub, a palette entry, and an Action Browser row badged "manifest only" — but it is NOT executable: invoking it surfaces `ACTION_NOT_FOUND` until a runtime `ctx.actions.register(...)` or `ctx.actions.registerFromCommand(...)` call in `activate()` binds the handler using the same id. Ship BOTH, exactly as `appos-plugin-ytdlp` does.

**Removal marker**: when you retire an action, remove BOTH sites — the runtime `register()` call and the manifest contribution. A leftover manifest stub is stale catalog/manifest metadata today, and once fn-163 lands it would be replayed into discovery at every cold start as a permanently non-executable palette entry.
**Removal marker**: when you retire an action, remove BOTH sites — the runtime `register()` call and the manifest contribution. A leftover manifest stub is replayed into discovery at every cold start as a permanently non-executable palette entry (`ACTION_NOT_FOUND` on invoke).

## 9. Write src/main.ts

Expand Down
10 changes: 5 additions & 5 deletions plugins/appos-dev/compiled/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"schema": 1,
"artifacts": {
"cli-chat-system-prompt.md": "0ace5e39d569fac84ca22e244bfb5dae8242b14e059395894f16f712b1ecc170",
"plugin-factory-context.md": "389a4fc99cdc0fee665ac63bc1600332865b6f3fd7ff5314bd18a54065bf5bec"
"plugin-factory-context.md": "2624e52479f40e97d9555a8b20f6c9a73cc32916d7776ce03c44f740e4cdd5b6"
},
"sources": {
"SKILL.md": "f4868a887ab7726ca8c6463b90953a1efb82712fb1a3df5e68888f9b43644cd6",
"reference/extension-api.md": "b37b24cd2a5e02345b361ee08aea29a885084ebfb4a8a73358036a91572c49e8",
"reference/patterns.md": "15be968192b19bbd74735a03571322df1a7c4fece579c6d2b3105eabb270f40a",
"reference/plugin-api/index.d.ts": "cbdd7a4aa96001d3fe664427ffc30039ce09ba80dca2eee32960c5c925c4d8d4",
"SKILL.md": "9ef85656be1a2f268a4ba52bc0a06413124732ba51f604d70a726419a6e873bc",
"reference/extension-api.md": "41c6224b238db928fcd22a912914d12048f65a21dd7fb7e437de123f6b439e10",
"reference/patterns.md": "3d70e16a476145c2d72e2893af5a2c7cafb07ee7f280ef62de91d8e7ddcc691c",
"reference/plugin-api/index.d.ts": "7299f52226d577aa8ad6160357a8dee2e02f90bb19a18f93ec31d605f5756b27",
"reference/plugin-api/core.d.ts": "3e951ef56e6148879fbe92d592399711f4bad5556f1549233ec3d32d0d9c49f9",
"reference/plugin-api/views.d.ts": "323f8570fe1c0e6140b5ee1f46cf79f368dc4d920e4b14544e5bf5753ae05f1e",
"reference/plugin-api/namespaces.d.ts": "467e6374fced48f027c22e9d01fe6daa68839b2f9881726725b901c66ccc8b1f",
Expand Down
67 changes: 33 additions & 34 deletions plugins/appos-dev/compiled/plugin-factory-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ void token; // keep for ctx.actions.unregister(token) on dispose
for an LLM reader. `registerFromCommand(commandId, metadata)` projects
an existing command into the catalog.
- Declare actions in the manifest `extensions[]` too, but ALWAYS pair with
the runtime registration — manifest-declared actions don't reach
discovery on their own yet (host bug fn-163; see
`reference/extension-api.md`). <!-- remove when fn-163 lands -->
the runtime registration — the manifest entry is replayed into discovery
(visible in `all()` / `palette.query()`, badged "manifest only") but
invoking it surfaces `ACTION_NOT_FOUND` until the runtime `register()`
binds the handler (see `reference/extension-api.md`).
- Scopes: `actions.register` to register, `actions.invoke` to invoke
(`actions.invoke.agent` additionally for agent-sourced invokes).

Expand Down Expand Up @@ -442,7 +443,7 @@ load. Full pattern: `reference/patterns.md` §13 + §23.

Two manifest families live in `reference/extension-api.md`: `extensions[]`
(manifest-declarative core-plugin contributions — qualified-id grammar,
per-EP payloads, fn-163 dual-registration caveat for actions) and
per-EP payloads, dual-registration contract for actions) and
`dependencies` (system binaries + plugin deps; full example:
`reference/patterns.md` §23).

Expand Down Expand Up @@ -711,22 +712,21 @@ Other methods:
|---|---|---|
| `registerFromCommand(commandId, metadata)` | Projects an existing `ctx.commands` command into the action catalog | `actions.register` |
| `invoke(id, input, source?)` | Invokes through the full pipeline; resolves to an `ActionReceipt` | `actions.invoke` (+ `actions.invoke.agent` for source `"agent"`) |
| `all()` | Lists the action catalog (runtime-registered only on the shipped host — see below) | `actions.list` |
| `all()` | Lists the merged action catalog (runtime-registered + manifest-declared — see below) | `actions.list` |
| `unregister(handleToken)` | Removes an executable registration | `actions.register` |

By design `all()` also returns **manifest-only** entries (declared via
`extensions[]` but with no executable handler bound yet; invoking one
surfaces `ACTION_NOT_FOUND` until `register()` binds the handler). On the
shipped host this does NOT happen — manifest `actions.definition`
contributions never reach discovery (host bug fn-163; see the caveat
under "`extensions[]`" below), so `all()` and `palette.query()` list
runtime-registered actions only. Never write code that expects to find an
unbound manifest action in `all()`. <!-- remove when fn-163 lands -->
`all()` also returns **manifest-only** entries — `actions.definition`
contributions are replayed into discovery at cold start and on plugin
activation, so they surface in `all()` and `palette.query()` (badged
"manifest only" in the Action Browser) before any runtime call. They are
metadata, not executables: invoking one surfaces `ACTION_NOT_FOUND` until
`register()` / `registerFromCommand()` binds the handler (see
"`extensions[]`" below).

#### `ctx.palette` — palette integration (fn-89)

`query(text, scope?)` searches the action catalog (the fn-163 caveat
above appliesruntime-registered actions only); `pin(id)` /
`query(text, scope?)` searches the merged action catalog (manifest-only
entries includedsee `all()` above); `pin(id)` /
`unpin(id)` manage palette pins (`palette.contribute.scope`);
`history(limit?)` returns recent invocations (`palette.history`).

Expand Down Expand Up @@ -979,12 +979,14 @@ conventionally under `contribution`:
AppOS repo — its manifest carries recipe + sequence + trigger
contributions and its `activate` is a no-op.

> **Caveat — manifest-declared ACTIONS don't reach discovery yet (host bug
> fn-163).** An `actions.definition` contribution alone currently never
> becomes palette-visible or invokable. Pair EVERY `actions.definition`
> contribution with a runtime `ctx.actions.register(...)` or
> `ctx.actions.registerFromCommand(...)` call — dual registration, exactly
> as `appos-plugin-ytdlp` ships it. <!-- remove when fn-163 lands -->
> **Manifest-declared ACTIONS are discoverable metadata, not executables.**
> The host replays `actions.definition` contributions into discovery at
> cold start and on plugin activation (visible in `ctx.actions.all()` /
> `palette.query()`, badged "manifest only"), but invoking one surfaces
> `ACTION_NOT_FOUND` until a runtime `ctx.actions.register(...)` or
> `ctx.actions.registerFromCommand(...)` call binds the handler. Pair
> EVERY contribution with its runtime registration — dual registration,
> exactly as `appos-plugin-ytdlp` ships it.

## Catalog bundle layout

Expand Down Expand Up @@ -1134,13 +1136,10 @@ Notable properties:
- **`remoteImage`** — `url` (file:// only in v1), `width`, `height`,
`cornerRadius`, `maxDimension` (default 512)
- **`textField`** — `placeholder`, `text` (initial contents), `action`
(fires on submit). Divergence: SDK 3.0.0 (`TextFieldDescriptor` and the
`textField()` builder) names the initial-contents property `text`, but
the shipped 1.0.0 host reads `value` — typed `text` compiles yet
renders empty today, while `value` fails excess-property checking. To
seed initial contents on today's host, include BOTH keys via an
assertion-cast properties object. <!-- collapse to `text`-only when the
host reads `text` -->
(fires on submit). The host reads `text` for the initial contents,
falling back to the legacy loose-JSON `value` key only for back-compat.
Write the typed `text` property (as `TextFieldDescriptor` and the
`textField()` builder do) — never add a `value` key.
- **`progress`** — `value` (0–1, omit for indeterminate), `label`,
`style` (`"bar"` | `"circular"`)
- **`listItem`** — `title`, `subtitle`, `icon`, `iconColor`, `action`,
Expand Down Expand Up @@ -1616,15 +1615,15 @@ export async function registerActions(ctx: PluginContext): Promise<() => Promise
- `register` resolves to a handle token — keep it for `unregister` in your
dispose path.

## 3. `extensions[]` + runtime dual registration (fn-163 workaround)
## 3. `extensions[]` + runtime dual registration

**Files**: `plugin.json` + `src/main.ts`

Declare actions in the manifest so they are visible in catalogs and
manifest scans — AND bind the executable at runtime. Manifest-declared
actions currently never reach discovery on their own (host bug fn-163), so
ship BOTH, exactly as `appos-plugin-ytdlp` does.
<!-- remove when fn-163 lands -->
Declare actions in the manifest AND bind the executable at runtime. The
manifest entry is replayed into discovery (palette-visible, badged
"manifest only") but stays non-executable — `ACTION_NOT_FOUND` on
invoke — until the runtime registration binds the handler. Ship BOTH,
exactly as `appos-plugin-ytdlp` does.

```json
{
Expand Down
9 changes: 5 additions & 4 deletions plugins/appos-dev/skills/appos-plugin-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,10 @@ void token; // keep for ctx.actions.unregister(token) on dispose
for an LLM reader. `registerFromCommand(commandId, metadata)` projects
an existing command into the catalog.
- Declare actions in the manifest `extensions[]` too, but ALWAYS pair with
the runtime registration — manifest-declared actions don't reach
discovery on their own yet (host bug fn-163; see
`reference/extension-api.md`). <!-- remove when fn-163 lands -->
the runtime registration — the manifest entry is replayed into discovery
(visible in `all()` / `palette.query()`, badged "manifest only") but
invoking it surfaces `ACTION_NOT_FOUND` until the runtime `register()`
binds the handler (see `reference/extension-api.md`).
- Scopes: `actions.register` to register, `actions.invoke` to invoke
(`actions.invoke.agent` additionally for agent-sourced invokes).

Expand Down Expand Up @@ -425,7 +426,7 @@ load. Full pattern: `reference/patterns.md` §13 + §23.

Two manifest families live in `reference/extension-api.md`: `extensions[]`
(manifest-declarative core-plugin contributions — qualified-id grammar,
per-EP payloads, fn-163 dual-registration caveat for actions) and
per-EP payloads, dual-registration contract for actions) and
`dependencies` (system binaries + plugin deps; full example:
`reference/patterns.md` §23).

Expand Down
Loading
Loading