Skip to content

Make pi-web extension API discoverable to agents - #87

Merged
ashwin-pc merged 2 commits into
mainfrom
docs/agent-extension-discovery
Aug 8, 2026
Merged

Make pi-web extension API discoverable to agents#87
ashwin-pc merged 2 commits into
mainfrom
docs/agent-extension-discovery

Conversation

@ashwin-pc

@ashwin-pc ashwin-pc commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses the post-merge follow-up from #86:

  • injects cwd-independent absolute pointers to the pi-web extension API docs and examples into every session's generated web context
  • explicitly identifies notepad.ts as the panel/invalidation exemplar
  • migrates every first-party browser UI example to the canonical contribute() API, so agents cannot copy legacy wrapper usage from bundled examples
  • adds context and example contract coverage for both discovery paths

The typed set* wrappers remain available only as compatibility boundaries for existing external extensions. First-party guidance now teaches one canonical implementation path.

This follows pi core's existing push-based documentation routing pattern: agents see where to read, while the full docs remain out of context until extension-authoring work triggers them.

Validation

  • npm run typecheck
  • npm run test:unit — 328 passed
  • npm run build

@ashwin-pc

Copy link
Copy Markdown
Owner Author

Reviewed on df6789a, independently verified: typecheck ✅, unit ✅ (328, incl. the new context/example contract tests), build ✅. Also checked the deployment angle: package.json#files ships both docs/ and examples/, so the injected absolute pointers resolve in npm installs, not just git checkouts. ✅

What's right:

  • The pointer block lands exactly where it belongs — generated at session creation from import.meta.url-derived appDir, appended to the web context, cwd-independent, zero context cost until triggered. Faithful to pi core's routing pattern.
  • Migrating the four examples instead of annotating them (my weaker suggestion) is the better call: "copy the examples" is the de-facto API doc, and the regex contract test (.contribute( present, no .set*() makes the canonical-idiom guarantee enforced rather than aspirational. Each migration is semantically faithful — I traced all four through the kernel adapters (artifact render({context}), header render(), footer view normalization, git-tab context→repo mapping); behavior is equivalent on current pi-web.

One real gap — inconsistent defensive posture, and it recreates the incident class:

The pointer says "notepad.ts shows the current contribute() API" — and the notepad's idiom includes capability checking. The four migrated examples don't: on a pre-#86 pi-web, ctx.ui.web.contribute is undefined and each crashes at session_start (git-footer worse: inside a timer .then(), where the rejection may bypass the extension error hook entirely). These are precisely the curl-from-main installables, and pre-#86 hosts are also the ones without the runtime-error surfacing — so the failure is silent, which is the exact class the live incident exposed. Two acceptable fixes, cheapest first:

  • one guard line per example: if (typeof ctx.ui.web.contribute !== "function") return; (matching the exemplar's taught idiom), or
  • a version-requirement note in each example header + the docs' install sections.

I'd do the guard: four lines total, and then all five first-party examples teach the same defensive registration story.

Two smaller notes:

  1. The context contract test greps source text but nothing asserts the referenced files exist — a future docs/examples move would silently dangle the pointer. One cheap test: existsSync(join(appDir, "docs/pi-web-extensions.md")) and the examples dir, computed the same way the service computes them.
  2. Behavior nuance from the git-footer migration: setFooter with an invalid/empty view silently cleared the footer; contribute() now throws (Footer contribution requires a valid view). Unreachable in practice here (the HTML is always non-empty when a repo is detected), but it's inside a timer chain — worth knowing it exists if a future edit makes empty HTML possible.

Verdict: approve once the compat guard (or explicit version note) is in — it's four lines, and it closes the loop on making the first-party examples teach all of the current idiom, defensive registration included.

@ashwin-pc

Copy link
Copy Markdown
Owner Author

Amending my review — the compat-guard ask is superseded.

The maintainer's counterpoint is correct: per-extension guard lines are a convention, and conventions diverge at exactly the rate extensions get added. Requiring them in four files today creates the first four instances of that divergence. Replacing the ask:

  1. Drop the four guard lines. Instead, one sentence in the docs' curl-install sections: examples on main require a pi-web with the contribution API (post-Publish contribution API and pull invalidation #86).
  2. The durable fix is declarative, loader-enforced requirements — separate issue, not this PR: extensions export requires = { capabilities: ["contribute", "slot:panel"] }; the loader checks before running extension code and records unmet requirements in the extensions status surface (Publish contribution API and pull invalidation #86's diagnostics machinery, reused). Vocabulary already exists (capabilities derives from the policies); a contract test can enforce that first-party examples declare. One check for N extensions — divergence becomes structurally impossible rather than conventionally discouraged.
  3. The notepad's capability check stays and is correctly per-extension — it's not crash prevention, it's graceful degradation (tool survives without UI), which only the extension itself can define. The two concerns were conflated in my original ask; only the second belongs in extension bodies.

With #1 (a docs line) this PR is approved from my side. The other two review notes (pointer-target existence test; footer empty-view nuance) stand as non-blocking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant