Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5898759
feat: capability core and WebMCP projection
JoviDeCroock Jul 10, 2026
ce5a243
feat: agent trust layer — Web Bot Auth, destructive confirmation flow…
JoviDeCroock Jul 10, 2026
62102c1
feat: opt-in llms.txt emission from the resolved app graph
JoviDeCroock Jul 10, 2026
2133369
feat: integrate the agentic surface — llms.txt capability discovery, …
JoviDeCroock Jul 10, 2026
1ccc1e8
docs: tell the agentic-web story and document how to test agent surfaces
JoviDeCroock Jul 10, 2026
65d24e2
feat: agent-surface testability — capability test host, eval --start,…
JoviDeCroock Jul 10, 2026
d515fc6
Fix capability trust and dev cache
JoviDeCroock Jul 11, 2026
bab7051
Fix capability confirmation headers
JoviDeCroock Jul 11, 2026
0f8cab1
feat: capability typegen, capability-graph decision log, operational …
JoviDeCroock Jul 11, 2026
1c2ed0b
Fix capability dispatch edge cases
JoviDeCroock Jul 11, 2026
b71f625
Fix capability validation and middleware results
JoviDeCroock Jul 11, 2026
0dbb91b
Reject inherited capability input properties
JoviDeCroock Jul 11, 2026
db14d2b
Reject non-JSON capability objects
JoviDeCroock Jul 11, 2026
86811cf
Fix capability expose verification
JoviDeCroock Jul 11, 2026
0623349
Harden capability verification
JoviDeCroock Jul 11, 2026
96c4260
Avoid capability verify false positives
JoviDeCroock Jul 11, 2026
b708297
Avoid executing capability literals during verify
JoviDeCroock Jul 11, 2026
0ca8e3c
Avoid executing capability projection literals
JoviDeCroock Jul 11, 2026
82286b3
Tie the capability surface together: one protocol home, capability fo…
JoviDeCroock Jul 11, 2026
3743b39
Harden capability validation and static extraction
JoviDeCroock Jul 11, 2026
9b5b8b0
Preserve quoted capability registries
JoviDeCroock Jul 11, 2026
ab39b1c
Parse quoted capability keys lexically
JoviDeCroock Jul 11, 2026
1aba8af
Reconcile capability surface with main's API typegen
JoviDeCroock Jul 22, 2026
914a08f
Fix capability form submission contracts
JoviDeCroock Jul 22, 2026
a8f2c28
Address capability-graph review findings
JoviDeCroock Jul 23, 2026
2cc118a
Harden default-export capability extraction
JoviDeCroock Jul 23, 2026
9c3a515
Exclude pracht typegen outputs from oxfmt
JoviDeCroock Jul 23, 2026
dd7c96d
Fix second-round review findings in agents validation and static anal…
JoviDeCroock Jul 23, 2026
06bd035
Fix capability verification and form coercion review findings
JoviDeCroock Jul 25, 2026
82a3cc6
Harden capability projection review findings
JoviDeCroock Jul 25, 2026
581bf41
Fix capability form and verification follow-ups
JoviDeCroock Jul 26, 2026
df41ebe
Align same-origin request helper with main
JoviDeCroock Jul 26, 2026
a5c1dcd
Fix capability form redirect navigation
JoviDeCroock Jul 26, 2026
d14f867
Consolidate capability changesets into one entry
JoviDeCroock Jul 26, 2026
d5dde93
Fix external capability form redirects
JoviDeCroock Jul 26, 2026
ec338f6
Fix capability isolation and Unicode validation
JoviDeCroock Jul 26, 2026
f131e8e
Fix capability projection edge cases
JoviDeCroock Jul 26, 2026
e37855b
Fix regex control-flow extraction
JoviDeCroock Jul 26, 2026
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
23 changes: 23 additions & 0 deletions .changeset/capability-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"@pracht/capabilities": minor
"@pracht/core": minor
"@pracht/vite-plugin": minor
"@pracht/cli": minor
"create-pracht": patch
---

The capability graph: define a typed application operation once and project it to every surface — server code, a generated HTTP endpoint, WebMCP page tools for in-browser agents, the human UI, and llms.txt discovery — with a built-in agent trust layer. See docs/CAPABILITIES.md, docs/AGENT_TRUST.md, docs/LLMS_TXT.md, and the decision log in docs/CAPABILITY_GRAPH.md.

**Capability core.** The new `@pracht/capabilities` package provides `defineCapability()`: a protocol-neutral operation with a dependency-free JSON Schema subset validator (unsupported keywords are rejected at definition time so they can never silently widen an exposed contract), effect classes (`read`/`write`/`destructive`), named middleware, and explicit exposure. Capabilities register in the app manifest via `defineApp({ capabilities: { ... } })` and are private by default. The package is also the single home of the wire protocol — `capabilityHttpPath()`, the confirmation and transport header names, the `CapabilityErrorCode` union, the envelope types, the schema→TypeScript printer, and the shared static extractor (`@pracht/capabilities/static`) — consumed by the framework, the Vite plugin, and the CLI so the contract cannot drift between packages. Static extraction masks regex literals during entry-point discovery, including regex expression statements after control-flow conditions, and accepts ECMAScript code-point escapes based on their numeric range rather than a fixed digit count.

Capability validation also enforces the JSON data model at every boundary, including unconstrained/additional properties and schema `const`, `default`, and `enum` values, and applies JSON Schema string lengths by Unicode code point, so multipart files, prototype-named fields, astral Unicode characters, and other JavaScript-only values cannot bypass or distort validation and destructive-call confirmation bindings.

The shared static extractor used by browser codegen and `pracht verify` ignores comments, string contents, and regex literals when locating capability definitions and registrations, parses both fixed-width and code-point Unicode escapes in inline literals, analyzes the module's default-exported capability, and scopes manifest extraction to the exported `defineApp()` configuration — so examples, commented-out code, or a helper capability defined earlier in the file cannot change the generated capability surface.

**Projections.** `@pracht/core` resolves the registry and runs one dispatch pipeline (input validation → named middleware → `run()` → output validation) behind every surface: request-scoped `invokeCapability()` for direct server use (loaders, API routes, middleware), `POST /api/capabilities/<name>` with a typed `{ ok, data | error }` envelope, CSRF protection, and production redaction (custom HTTP paths that URL parsing could reinterpret as cross-origin or as a different pathname are rejected), and — via `@pracht/vite-plugin` — the generated `virtual:pracht/capabilities` browser client (`callCapability()`, with `confirm` sugar for confirmation tokens) and `virtual:pracht/webmcp`, a feature-detected WebMCP page-tool shim (`document.modelContext.registerTool`, Chrome origin trial). Direct invocation hosts are bound to their incoming `Request`, so overlapping apps or dev-server generations cannot route a call through another registry. Both virtual modules cost zero bytes when unused.

**One contract for humans and agents.** `<Form capability="notes.create">` posts the framework's form component straight to the capability endpoint agents call: fields are coerced onto the input schema server-side, `onCapabilityResult` receives the typed envelope, and without JavaScript the endpoint accepts the form-encoded post and answers a successful document submission with a 303 back to the same-origin referring page. Enhanced submissions honor a clicked submitter's `formaction` and follow middleware redirects to their final browser URL, matching that no-JavaScript behavior: a redirect is handed back to the same-origin fetch as a readable target (with relative `Location` values resolved against the endpoint) and the browser navigates itself, so an external OAuth/SSO destination is never fetched through CORS and never submitted twice, and a cross-origin form target falls back to a native document submission (after client-side schema validation, if any). Effect classes drive the client cache: after any successful non-`read` browser call (`callCapability()` or `<Form capability>`) the active route's loader data revalidates automatically — a full reload under islands hydration — and `revalidate: false` opts out per call.

**Agent trust layer.** Web Bot Auth verification (RFC 9421 HTTP Message Signatures, Ed25519 via WebCrypto, static keys or allowlisted `/.well-known/http-message-signatures-directory` JWKS lookups — fail closed everywhere) opts in via `defineApp({ agents: { webBotAuth } })` and surfaces the verified identity as `context.agent` — now typed end to end (`CapabilityContext`, `PrachtRequestContext`) with `"observe"`/`"require"` policies and per-capability `agentPolicy` overrides. Destructive capabilities may expose over HTTP only, gated by a server-verified prepare/commit confirmation flow (`409 confirmation_required` + short-lived HMAC token bound to principal, capability, and canonical input; requires `PRACHT_CONFIRMATION_SECRET`). The gate runs inside the named middleware chain, so rate limiting sees prepare and invalid-token attempts too. Every dispatch emits a structured audit event (`setCapabilityAuditHook()` / `onCapabilityAudit`) whose transport distinguishes `http`, `server`, and `webmcp`.

**Discovery & DX.** The opt-in `pracht({ llmsTxt })` option emits llms.txt (https://llmstxt.org) from the resolved app graph — pages, API endpoints, and HTTP-exposed capabilities with effect classes — written at build time and served live in dev; `create-pracht` templates enable it by default. `pracht typegen` emits `src/pracht-capabilities.d.ts` so `invokeCapability()`, `callCapability()`, `<Form capability>`, and the test host infer input/output types from the capability name. `pracht eval` runs scripted agent-task scenarios (with `$steps[n]` references and a `confirm` field for the confirmation flow) against a live app, `--start` managing the server lifecycle. `createCapabilityTestHost()` unit-tests the full pipeline including simulated agent identities. `pracht inspect capabilities`, the MCP `inspect_capabilities` tool, `/_pracht` devtools, and the dev banner all render the same graph — with declared-but-unserved `expose.mcp` labeled `mcp(unserved)` and warned about by `pracht verify` until the remote MCP projection ships.
9 changes: 8 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"ignorePatterns": ["**/*.md", "**/package.json", "**/LICENSE"]
"ignorePatterns": [
"**/*.md",
"**/package.json",
"**/LICENSE",
"**/pracht.d.ts",
"**/pracht-routes.ts",
"**/pracht-capabilities.d.ts"
]
}
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The starter gives you:
- `pracht dev` — local SSR + HMR, a `/_pracht` devtools page with the resolved route/API graph (JSON at `/_pracht.json`), and `Server-Timing` middleware/loader/render phase timings on every dev SSR response
- `pracht build` — client/server output plus SSG/ISG prerendering, with `--analyze` for a per-route client JS report and budget enforcement
- `pracht preview` — build and serve the production build locally
- `pracht inspect [routes|api|build] --json` — resolved app graph metadata
- `pracht inspect [routes|api|capabilities|build] --json` — resolved app graph metadata
- `pracht generate route|shell|middleware|api` — framework-native scaffolding; `generate route` also emits a Playwright smoke test when the app has an e2e setup
- `pracht verify` — fast framework-aware checks with `--changed` and `--json`, including `defineApp({ constraints })` enforcement and app-graph snapshot freshness
- `pracht plan` — semantic app-graph diff against a base git ref (`--write` refreshes the committed `.pracht/app-graph.json` snapshot; `--markdown` for PR comments)
Expand All @@ -107,6 +107,9 @@ Pracht is built to be operated by coding agents as much as by humans — and for
- **Machine-enforced invariants** — `defineApp({ constraints })` declares rules like `requireMiddleware("/app/**", "auth")` that `pracht verify` enforces deterministically, so no author (human or LLM) can merge a violation.
- **MCP server** — `pracht mcp` starts a stdio [Model Context Protocol](https://modelcontextprotocol.io) server so agents can natively inspect the resolved app graph, run doctor/verify diagnostics, diff and snapshot the graph (plan/report), read the authoring guide (get_docs), and scaffold routes, shells, middleware, and API handlers. See [docs/MCP.md](docs/MCP.md) for registration and the tool reference.
- **Authoring guide for agents** — `pracht llms --write` drops the framework's conventions into `llms.txt` so any coding agent picks them up.
- **Capabilities & WebMCP** — `@pracht/capabilities` lets you define a typed application operation once (JSON Schema contract, effect class, middleware) and project it to server code, a generated HTTP endpoint, a WebMCP page tool for in-browser agents, and the human UI via `<Form capability>` — private by default, with `pracht verify` enforcing the security defaults and effect-driven revalidation keeping the page consistent after mutations. See [docs/CAPABILITIES.md](docs/CAPABILITIES.md).
- **Agent trust layer** — Web Bot Auth (RFC 9421) verified agent identity on the request context with observe/require policies, a prepare/commit confirmation flow for destructive capabilities, capability audit events, and `pracht eval` for scripted agent-task checks in CI. See [docs/AGENT_TRUST.md](docs/AGENT_TRUST.md).
- **llms.txt** — the opt-in `llmsTxt` plugin option emits an [llms.txt](https://llmstxt.org) index of your pages, API routes, and HTTP-exposed capabilities at build time and serves it live in dev. See [docs/LLMS_TXT.md](docs/LLMS_TXT.md).
- **Claude Code skills** — 28 skills for scaffolding, auditing, testing, debugging, and deploying pracht apps live in [skills/](skills/README.md). See the [agent skills](#agent-skills) section below.

## Agent skills
Expand All @@ -116,7 +119,6 @@ The skills are distributed three ways ([docs](https://pracht.resynapse.dev/docs/
- **Discovery endpoint** — every skill is published at `https://pracht.resynapse.dev/skills/<name>/SKILL.md`, listed with SHA-256 digests in the manifest at [`/.well-known/agent-skills/index.json`](https://pracht.resynapse.dev/.well-known/agent-skills/index.json) and advertised via a `Link: rel="agent-skills"` header.
- **create-pracht** — `npm create pracht@latest` seeds the full catalog into new apps' `.claude/skills/` and writes a `.mcp.json` registering the `pracht mcp` server (yes-default prompt, `--no-agent-tools` to skip).
- **In this repo** — `.claude/skills` symlinks to [skills/](skills/README.md), so Claude Code loads them automatically for contributors.
>>>>>>> 463a134 (Ship the skills to users: create-pracht seeding, docs page, contributor loading)

## Repo map

Expand All @@ -127,6 +129,10 @@ The skills are distributed three ways ([docs](https://pracht.resynapse.dev/docs/
- [docs/PERFORMANCE.md](docs/PERFORMANCE.md) — bundle analysis and per-route client JS budgets
- [docs/DATA_LOADING.md](docs/DATA_LOADING.md) — loaders, forms, client hooks
- [docs/API_VALIDATION.md](docs/API_VALIDATION.md) — Standard Schema validation for API routes, typed `apiFetch()`
- [docs/CAPABILITIES.md](docs/CAPABILITIES.md) — typed capabilities, HTTP projection, WebMCP page tools
- [docs/AGENT_TRUST.md](docs/AGENT_TRUST.md) — Web Bot Auth, effect-class confirmation flow, audit hook, `pracht eval`
- [docs/CAPABILITY_GRAPH.md](docs/CAPABILITY_GRAPH.md) — the capability graph product bet: proposal, decision log, staged plan
- [docs/LLMS_TXT.md](docs/LLMS_TXT.md) — generated llms.txt: pages, API endpoints, capabilities
- [docs/STYLING.md](docs/STYLING.md) — CSS Modules, Tailwind, CSS-in-JS limitations
- [docs/ADAPTERS.md](docs/ADAPTERS.md) — Node, Cloudflare, Vercel deployment paths
- [docs/MCP.md](docs/MCP.md) — built-in MCP server for coding agents
Expand Down
34 changes: 34 additions & 0 deletions VISION_MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,40 @@ Standalone server endpoints independent of the page rendering pipeline:
compile time and returns typed responses. See
[docs/API_VALIDATION.md](docs/API_VALIDATION.md).

### Capabilities (agent-ready operations)

Typed, protocol-neutral application operations registered in the manifest via
`defineApp({ capabilities: { ... } })` and defined with `defineCapability()`
from `@pracht/capabilities`:

- One contract (JSON Schema input/output, effect class, named middleware,
server-only `run()`), projected to direct server invocation
(`invokeCapability()`), a generated HTTP endpoint (`expose.http`), and a
WebMCP page tool for in-browser agents (`expose.webmcp`).
- The same contract serves the human UI: `<Form capability>` posts to the
capability endpoint (with a no-JS form-encoded fallback), and successful
non-`read` browser calls revalidate route data automatically — the effect
class drives the client cache. The wire protocol (paths, headers, error
codes, envelope) has a single home in `@pracht/capabilities`.
- Private by default; exposure requires a complete contract, `destructive`
effects are HTTP-only behind a server-verified prepare/commit confirmation
flow, and the graph feeds `pracht inspect capabilities`, `/_pracht`,
`pracht verify`, and the CLI MCP server.
- Agent trust layer: Web Bot Auth (RFC 9421) verified agent identity as
`context.agent` with observe/require policies, capability audit events,
and a `pracht eval` harness for scripted agent-task checks.
- Discovery: the opt-in `llmsTxt` plugin option emits an
[llms.txt](https://llmstxt.org) index from the app graph — pages, API
endpoints, and HTTP-exposed capabilities
([docs/LLMS_TXT.md](docs/LLMS_TXT.md)).
- `pracht typegen` generates capability input/output types from the graph, so
`invokeCapability()` and the browser's `callCapability()` infer both sides
from the capability name.
- See [docs/CAPABILITIES.md](docs/CAPABILITIES.md) and
[docs/AGENT_TRUST.md](docs/AGENT_TRUST.md). The product bet, its decision
log, and the staged plan (remote MCP and MCP Apps are the unbuilt stages)
live in [docs/CAPABILITY_GRAPH.md](docs/CAPABILITY_GRAPH.md).

### Deployment Adapters

Platform adapters export a request handler shaped for their runtime:
Expand Down
Loading