@nifrajs/cli 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +10 -0
  2. package/dist/assure-hydration.d.ts.map +1 -1
  3. package/dist/assure-hydration.js +6 -1
  4. package/dist/assure-hydration.js.map +1 -1
  5. package/dist/assure.d.ts.map +1 -1
  6. package/dist/assure.js +3 -2
  7. package/dist/assure.js.map +1 -1
  8. package/dist/check-diagnostics.d.ts +165 -0
  9. package/dist/check-diagnostics.d.ts.map +1 -0
  10. package/dist/check-diagnostics.js +1009 -0
  11. package/dist/check-diagnostics.js.map +1 -0
  12. package/dist/check-scan.d.ts +245 -0
  13. package/dist/check-scan.d.ts.map +1 -0
  14. package/dist/check-scan.js +1599 -0
  15. package/dist/check-scan.js.map +1 -0
  16. package/dist/check.d.ts +4 -324
  17. package/dist/check.d.ts.map +1 -1
  18. package/dist/check.js +8 -2366
  19. package/dist/check.js.map +1 -1
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +247 -10
  22. package/dist/cli.js.map +1 -1
  23. package/dist/command-catalog.d.ts +21 -1
  24. package/dist/command-catalog.d.ts.map +1 -1
  25. package/dist/command-catalog.js +122 -0
  26. package/dist/command-catalog.js.map +1 -1
  27. package/dist/env-file.js +5 -5
  28. package/dist/env-file.js.map +1 -1
  29. package/dist/frontend-guidance.d.ts +57 -0
  30. package/dist/frontend-guidance.d.ts.map +1 -0
  31. package/dist/frontend-guidance.js +195 -0
  32. package/dist/frontend-guidance.js.map +1 -0
  33. package/dist/levels-tool.d.ts.map +1 -1
  34. package/dist/levels-tool.js +6 -0
  35. package/dist/levels-tool.js.map +1 -1
  36. package/dist/load.d.ts.map +1 -1
  37. package/dist/load.js +4 -1
  38. package/dist/load.js.map +1 -1
  39. package/dist/mcp-context.d.ts +48 -0
  40. package/dist/mcp-context.d.ts.map +1 -0
  41. package/dist/mcp-context.js +215 -0
  42. package/dist/mcp-context.js.map +1 -0
  43. package/dist/mcp-docs-tools.d.ts.map +1 -1
  44. package/dist/mcp-docs-tools.js +31 -0
  45. package/dist/mcp-docs-tools.js.map +1 -1
  46. package/dist/mcp-exec.d.ts +74 -0
  47. package/dist/mcp-exec.d.ts.map +1 -0
  48. package/dist/mcp-exec.js +844 -0
  49. package/dist/mcp-exec.js.map +1 -0
  50. package/dist/mcp-http.js +1 -1
  51. package/dist/mcp-io.d.ts +26 -0
  52. package/dist/mcp-io.d.ts.map +1 -0
  53. package/dist/mcp-io.js +100 -0
  54. package/dist/mcp-io.js.map +1 -0
  55. package/dist/mcp-reflect.d.ts +16 -0
  56. package/dist/mcp-reflect.d.ts.map +1 -0
  57. package/dist/mcp-reflect.js +101 -0
  58. package/dist/mcp-reflect.js.map +1 -0
  59. package/dist/mcp-root.d.ts +7 -0
  60. package/dist/mcp-root.d.ts.map +1 -1
  61. package/dist/mcp-root.js +51 -3
  62. package/dist/mcp-root.js.map +1 -1
  63. package/dist/mcp.d.ts +10 -92
  64. package/dist/mcp.d.ts.map +1 -1
  65. package/dist/mcp.js +13 -1232
  66. package/dist/mcp.js.map +1 -1
  67. package/dist/openapi-tool.d.ts +5 -10
  68. package/dist/openapi-tool.d.ts.map +1 -1
  69. package/dist/openapi-tool.js +21 -2
  70. package/dist/openapi-tool.js.map +1 -1
  71. package/dist/openapi-types.d.ts +21 -0
  72. package/dist/openapi-types.d.ts.map +1 -0
  73. package/dist/openapi-types.js +214 -0
  74. package/dist/openapi-types.js.map +1 -0
  75. package/dist/port.d.ts.map +1 -1
  76. package/dist/port.js +2 -1
  77. package/dist/port.js.map +1 -1
  78. package/dist/project-facts.d.ts +2 -1
  79. package/dist/project-facts.d.ts.map +1 -1
  80. package/dist/project-facts.js.map +1 -1
  81. package/dist/release-verification.d.ts +4 -1
  82. package/dist/release-verification.d.ts.map +1 -1
  83. package/dist/release-verification.js +6 -138
  84. package/dist/release-verification.js.map +1 -1
  85. package/dist/rules/codes.d.ts +4 -0
  86. package/dist/rules/codes.d.ts.map +1 -1
  87. package/dist/rules/codes.js +4 -0
  88. package/dist/rules/codes.js.map +1 -1
  89. package/dist/rules/islands.d.ts +4 -0
  90. package/dist/rules/islands.d.ts.map +1 -0
  91. package/dist/rules/islands.js +155 -0
  92. package/dist/rules/islands.js.map +1 -0
  93. package/dist/rules/nano.d.ts +6 -0
  94. package/dist/rules/nano.d.ts.map +1 -0
  95. package/dist/rules/nano.js +264 -0
  96. package/dist/rules/nano.js.map +1 -0
  97. package/dist/rules/security.d.ts.map +1 -1
  98. package/dist/rules/security.js +38 -4
  99. package/dist/rules/security.js.map +1 -1
  100. package/dist/scaffold.d.ts +12 -6
  101. package/dist/scaffold.d.ts.map +1 -1
  102. package/dist/scaffold.js +149 -10
  103. package/dist/scaffold.js.map +1 -1
  104. package/dist/search-terms.d.ts.map +1 -1
  105. package/dist/search-terms.js +5 -3
  106. package/dist/search-terms.js.map +1 -1
  107. package/dist/stylex-migrate.d.ts +38 -0
  108. package/dist/stylex-migrate.d.ts.map +1 -0
  109. package/dist/stylex-migrate.js +1052 -0
  110. package/dist/stylex-migrate.js.map +1 -0
  111. package/dist/sync-manifest.d.ts.map +1 -1
  112. package/dist/sync-manifest.js +2 -1
  113. package/dist/sync-manifest.js.map +1 -1
  114. package/dist/verification-plan.d.ts +24 -0
  115. package/dist/verification-plan.d.ts.map +1 -0
  116. package/dist/verification-plan.js +116 -0
  117. package/dist/verification-plan.js.map +1 -0
  118. package/dist/work-graph.d.ts.map +1 -1
  119. package/dist/work-graph.js +6 -3
  120. package/dist/work-graph.js.map +1 -1
  121. package/docs/examples.json +46 -4
  122. package/docs/llms-full.txt +1346 -28
  123. package/docs/types.json +4185 -87
  124. package/package.json +9 -9
  125. package/src/assure-hydration.ts +6 -1
  126. package/src/assure.ts +4 -2
  127. package/src/check-diagnostics.ts +1219 -0
  128. package/src/check-scan.ts +1825 -0
  129. package/src/check.ts +47 -2752
  130. package/src/cli.ts +252 -10
  131. package/src/command-catalog.ts +155 -0
  132. package/src/env-file.ts +5 -5
  133. package/src/frontend-guidance.ts +259 -0
  134. package/src/levels-tool.ts +6 -0
  135. package/src/load.ts +4 -1
  136. package/src/mcp-context.ts +266 -0
  137. package/src/mcp-docs-tools.ts +37 -0
  138. package/src/mcp-exec.ts +1040 -0
  139. package/src/mcp-http.ts +1 -1
  140. package/src/mcp-io.ts +118 -0
  141. package/src/mcp-reflect.ts +140 -0
  142. package/src/mcp-root.ts +62 -4
  143. package/src/mcp.ts +61 -1511
  144. package/src/openapi-tool.ts +32 -2
  145. package/src/openapi-types.ts +261 -0
  146. package/src/port.ts +2 -1
  147. package/src/project-facts.ts +2 -4
  148. package/src/release-verification.ts +22 -164
  149. package/src/rules/codes.ts +4 -0
  150. package/src/rules/islands.ts +171 -0
  151. package/src/rules/nano.ts +295 -0
  152. package/src/rules/security.ts +40 -4
  153. package/src/scaffold.ts +169 -9
  154. package/src/search-terms.ts +7 -3
  155. package/src/stylex-migrate.ts +1139 -0
  156. package/src/sync-manifest.ts +2 -1
  157. package/src/verification-plan.ts +242 -0
  158. package/src/work-graph.ts +6 -3
@@ -958,15 +958,48 @@ function Row({ id }: { id: string }) {
958
958
  const anyPending = useFetchers().some((f) => f.snapshot().pending)
959
959
  ```
960
960
 
961
- ## Nifra - Rendering: SSG & ISR
961
+ ## Nifra - Rendering: SSR, streaming, SSG & ISR
962
962
 
963
- > Prerender static routes, enumerate dynamic ones, and cache rendered pages with stale-while-revalidate - on every runtime including the edge.
963
+ > Render critical HTML first, stream deferred data progressively, prerender static routes, and cache rendered pages with stale-while-revalidate - on every runtime including the edge.
964
964
 
965
965
  Nifra renders on one framework-agnostic seam, so the same app can be server-rendered per
966
- request (the default), **prerendered** to static files at build (SSG), or cached and served
966
+ request (the default), streamed progressively, **prerendered** to static files at build (SSG), or cached and served
967
967
  **stale-while-revalidate** (ISR) - and every strategy works on Bun, Node, Deno, **and**
968
968
  the edge.
969
969
 
970
+ ### Progressive rendering with `defer()`
971
+
972
+ Keep critical loader data in the first HTML shell and mark slower, non-critical values with
973
+ `defer()`. Nifra flushes the document shell and the adapter's rendered stream first,
974
+ then sends each deferred resolution through `<Await>` without a second data
975
+ fetch. The same protocol works for actions and soft navigations, including NDJSON on the client
976
+ side.
977
+
978
+ ```ts
979
+ import { defer } from "@nifrajs/web"
980
+
981
+ export async function loader({ api }: LoaderArgs<typeof app>) {
982
+ return {
983
+ product: (await api.products.get()).data, // critical: rendered in the first shell
984
+ reviews: defer(api.reviews.get()), // non-critical: streamed when ready
985
+ }
986
+ }
987
+
988
+ export default function Product(props: { data: LoaderData<typeof loader> }) {
989
+ return (
990
+ <>
991
+ <ProductSummary product={props.data.product} />
992
+ <Await resolve={props.data.reviews} fallback={<p>Loading reviews…</p>}>
993
+ {(reviews) => <Reviews items={reviews} />}
994
+ </Await>
995
+ </>
996
+ )
997
+ }
998
+ ```
999
+
1000
+ This is request-time progressive SSR/partial rendering. It is distinct from build-time SSG:
1001
+ the shell is not a precomputed PPR artifact, and deferred values remain request-scoped.
1002
+
970
1003
  ### SSG - prerender at build
971
1004
 
972
1005
  Opt a static route into prerendering with `export const prerender = true`. Its
@@ -1225,6 +1258,374 @@ if (document.documentElement.hasAttribute("data-nifra-hydrated")) start()
1225
1258
  else document.addEventListener("nifra:hydrated", start, { once: true })
1226
1259
  ```
1227
1260
 
1261
+ ## Nifra - Islands: the AI-safe interactivity lane
1262
+
1263
+ > Add interactivity to a zero-runtime vanilla page with islands - imperative DOM enhancers, no framework runtime, no hydration. Explicit code an AI (or a human) gets right first try: mount, coordinate over a typed bus, always return cleanup.
1264
+
1265
+ A `@nifrajs/web-vanilla` page ships zero framework runtime. When one corner needs
1266
+ to be interactive, you don&apos;t turn on hydration - you mount an **island**: a
1267
+ small, plain-DOM enhancer over a server-rendered element. Explicit imperative code with no
1268
+ hidden reactivity - the shape an AI agent (or a human) gets right on the first try.
1269
+
1270
+ ### Why islands, not hydration
1271
+
1272
+ Vanilla routes set `export const hydrate = false` - there is no client framework
1273
+ to hydrate with, and that is the point. The five framework adapters give you a reactive
1274
+ runtime and pay for it in client JS and in the subtle failure modes (stale closures,
1275
+ hydration mismatch, what-re-runs-when) that trip up generated code. Islands take the other
1276
+ trade: no runtime, no reactivity to get wrong. You wire the DOM by hand and hand back a
1277
+ teardown. Reach for a framework adapter when you genuinely need a stateful app UI; reach for
1278
+ islands for widgets on an otherwise-static page.
1279
+
1280
+ ### Mount an island
1281
+
1282
+ Render a `<nifra-island>` marker in your page (props are inline JSON), then
1283
+ bundle one entry that calls `mountIslands`. Load it via the route&apos;s
1284
+ `islandScripts`.
1285
+
1286
+ ```ts
1287
+ // doc-check: skip - server-rendered island marker (HTML, not a checkable module).
1288
+ // In your @nifrajs/web-vanilla page, render the host element with inline JSON props:
1289
+ html`<nifra-island data-id="counter" data-props=${JSON.stringify({ start: 0 })}>
1290
+ <output>0</output>
1291
+ <button type="button">+1</button>
1292
+ </nifra-island>`
1293
+ ```
1294
+
1295
+ ```ts
1296
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1297
+ import { defineIsland, mountIslands } from "@nifrajs/web/islands"
1298
+
1299
+ // defineIsland pins the data-props type; the body is plain imperative DOM. ALWAYS return the
1300
+ // teardown for anything you wire - mountIslands calls it on soft-nav (NF-C020 warns if you forget).
1301
+ const counter = defineIsland<{ start: number }>((el, props) => {
1302
+ let n = props.start
1303
+ const out = el.querySelector("output")!
1304
+ const onClick = () => { out.textContent = String(++n) }
1305
+ el.querySelector("button")?.addEventListener("click", onClick)
1306
+ return () => el.querySelector("button")?.removeEventListener("click", onClick)
1307
+ })
1308
+
1309
+ // One bundle entry, loaded via the route's `islandScripts`.
1310
+ mountIslands({ counter })
1311
+ ```
1312
+
1313
+ `defineIsland<Props>` is a zero-cost identity helper - its only job is to
1314
+ type `props` and `el` so the body needs no cast. Keep the body
1315
+ imperative: read the DOM, add listeners, **return the cleanup**. The returned
1316
+ function runs on soft-nav teardown; forget it and every navigation leaks the listener -
1317
+ `nifra check` flags exactly that as `NF-C020`.
1318
+
1319
+ ### Coordinate islands with a typed bus
1320
+
1321
+ Islands are isolated by design - each enhances one element and shares no state. When two must
1322
+ talk (an &ldquo;add to cart&rdquo; button and a cart badge), use a
1323
+ `createIslandBus`: one typed channel, created once and closed over by each
1324
+ enhancer. No implicit global, so concurrent renders and tests never cross-talk.
1325
+
1326
+ ```ts
1327
+ import { createIslandBus } from "@nifrajs/web/islands"
1328
+
1329
+ // One typed channel, created once and closed over by every enhancer that needs it.
1330
+ type CartEvents = { "cart:add": { sku: string }; "cart:count": number }
1331
+ const bus = createIslandBus<CartEvents>()
1332
+
1333
+ const off = bus.on("cart:count", (n) => console.log("cart now has", n, "items"))
1334
+ bus.emit("cart:add", { sku: "ABC-1" })
1335
+ off() // unsubscribe - also usable directly as an island's cleanup
1336
+ ```
1337
+
1338
+ The bus is synchronous, in-memory, and carries no history - a subscriber sees only events
1339
+ emitted after it subscribes. A throwing handler is isolated from the others. Because
1340
+ `on()` returns its own unsubscribe, an enhancer whose only job is to listen can
1341
+ return it directly as its cleanup:
1342
+
1343
+ ```ts
1344
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1345
+ import { createIslandBus, defineIsland, mountIslands } from "@nifrajs/web/islands"
1346
+
1347
+ type CartEvents = { "cart:add": { sku: string }; "cart:count": number }
1348
+ const bus = createIslandBus<CartEvents>()
1349
+
1350
+ // Two islands that never import each other - they meet only on the bus.
1351
+ const addButton = defineIsland<{ sku: string }>((el, props) => {
1352
+ const onClick = () => bus.emit("cart:add", { sku: props.sku })
1353
+ el.addEventListener("click", onClick)
1354
+ return () => el.removeEventListener("click", onClick)
1355
+ })
1356
+
1357
+ let count = 0
1358
+ const badge = defineIsland((el) => {
1359
+ const unsubscribe = bus.on("cart:add", () => {
1360
+ count += 1
1361
+ el.textContent = String(count)
1362
+ bus.emit("cart:count", count)
1363
+ })
1364
+ return unsubscribe // the on() unsubscribe IS the cleanup
1365
+ })
1366
+
1367
+ mountIslands({ addButton, badge })
1368
+ ```
1369
+
1370
+ ### Pattern: filter drives results
1371
+
1372
+ The same shape scales to any &ldquo;one island changes, another reacts&rdquo; case. Each
1373
+ island owns only its own DOM; they meet on the channel, never in a shared store.
1374
+
1375
+ ```ts
1376
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1377
+ import { createIslandBus, defineIsland, mountIslands } from "@nifrajs/web/islands"
1378
+
1379
+ // filter -> results, one directional channel. The results island owns its own DOM; the filter
1380
+ // island owns none of it. No shared store, no reactive tree - a message and two enhancers.
1381
+ const bus = createIslandBus<{ "filter:set": string }>()
1382
+
1383
+ const filter = defineIsland((el) => {
1384
+ const input = el.querySelector("input")!
1385
+ const onInput = () => bus.emit("filter:set", input.value.toLowerCase())
1386
+ input.addEventListener("input", onInput)
1387
+ return () => input.removeEventListener("input", onInput)
1388
+ })
1389
+
1390
+ const results = defineIsland((el) =>
1391
+ bus.on("filter:set", (q) => {
1392
+ for (const row of el.querySelectorAll<HTMLElement>("[data-name]")) {
1393
+ const name = row.dataset.name ?? ""
1394
+ row.hidden = q !== "" && !name.toLowerCase().includes(q)
1395
+ }
1396
+ }),
1397
+ )
1398
+
1399
+ mountIslands({ filter, results })
1400
+ ```
1401
+
1402
+ ### Load strategies
1403
+
1404
+ A marker&apos;s `data-strategy` defers its enhancer: `load` (default),
1405
+ `idle`, `visible` (mount when it scrolls into view), or a
1406
+ `media` query. An island with no matching enhancer, or an invalid strategy, stays
1407
+ inert server-rendered HTML - forward-compatible, never a runtime error.
1408
+
1409
+ ## Nifra - nano: explicit reactivity for small apps
1410
+
1411
+ > nano is the AI-safe small-app lane: signal + computed(fn, [deps]) + keyed bindList, no VDOM, no auto-tracking. Every reactive edge is a visible call, so its three mistakes are static lints (NF-C021/C022/C023) with a fix recipe - the same closed loop that makes the backend AI-safe, now on the frontend.
1412
+
1413
+ `@nifrajs/web/nano` is the middle lane between a static
1414
+ [island](/docs/islands) and a full framework adapter. It gives you a signal, a
1415
+ derived `computed`, and a keyed list - enough to build a small app with local
1416
+ state a human edits - and nothing else. No virtual DOM, no template compiler, no
1417
+ auto-tracking. Every reactive edge is a call you write, which is exactly what lets a tool
1418
+ catch the mistakes before runtime.
1419
+
1420
+ ### Why a fourth lane
1421
+
1422
+ A framework fails generated code not because it is unfamiliar but because its reactivity is
1423
+ _implicit_: an effect silently tracks what it read, a stale closure captures the wrong
1424
+ value, a render mismatches hydration - all silent, all runtime. Preact fixes the
1425
+ **size** of that model (3 KB) but copies its footguns faithfully. nano
1426
+ fixes the **failure model** for the small-app band: dependencies are an explicit
1427
+ array, list keys are a required argument, cleanups are a returned value. Each of those is a
1428
+ thing a static check can see. Reach for nano when a page needs local state and a keyed list;
1429
+ reach for a [framework adapter](/docs/frameworks) the moment you need client
1430
+ routing, nested view state, or suspense.
1431
+
1432
+ ### Signals and computed
1433
+
1434
+ A `signal` holds a value; `.get()` reads it and `.set(...)`
1435
+ replaces it (deduped with `Object.is`). A `computed` derives a value and
1436
+ declares its sources as an explicit deps array - it recomputes when a listed source changes,
1437
+ never by magic. Both are synchronous and framework-free.
1438
+
1439
+ ```ts
1440
+ import { signal, computed } from "@nifrajs/web/nano"
1441
+
1442
+ // State is a signal: reads are .get(), writes are .set(...). Every edge is a call you can see.
1443
+ const count = signal(0)
1444
+
1445
+ // Derived state declares its dependencies EXPLICITLY - the array is the whole contract. It
1446
+ // recomputes when (and only when) a listed source changes. Object.is-deduped, like signal.
1447
+ const doubled = computed(() => count.get() * 2, [count])
1448
+
1449
+ const off = doubled.subscribe((v) => console.log("doubled is now", v))
1450
+ count.set(5) // logs: doubled is now 10
1451
+ off() // unsubscribe
1452
+ ```
1453
+
1454
+ The deps array is the whole contract, and its one failure mode is catchable: a
1455
+ `computed` whose body reads a signal the array omits will never recompute when that
1456
+ signal changes. `nifra check` flags exactly that as `NF-C023` - the
1457
+ check reads which signals the body calls `.get()` on and compares them to the
1458
+ array. A framework&apos;s auto-tracked effect gives a tool nothing to compare, which is why
1459
+ the equivalent lint elsewhere is a best-effort warning, not a gate.
1460
+
1461
+ ### Bind a signal to the DOM
1462
+
1463
+ `bind` is the one-value edge: it applies your function immediately and on every
1464
+ change, and returns a disposer. **Collect the disposer.** A bare
1465
+ `bind(...)` whose return value is discarded leaks its subscription on soft-nav -
1466
+ `nifra check` flags it as `NF-C021`.
1467
+
1468
+ ```ts
1469
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1470
+ import { signal, bind } from "@nifrajs/web/nano"
1471
+
1472
+ const label = signal("hello")
1473
+
1474
+ // bind applies immediately AND on every change; it returns the disposer. COLLECT it - a bare
1475
+ // bind(...) that drops its return leaks the subscription on soft-nav (nifra check flags NF-C021).
1476
+ const off = bind(document.querySelector("h1")!, label, (el, v) => { el.textContent = v })
1477
+ label.set("world") // the <h1> updates synchronously
1478
+ // ...on teardown: off()
1479
+ ```
1480
+
1481
+ ### Keyed lists with bindList
1482
+
1483
+ `bindList` keeps a container&apos;s children in sync with an array by key - a keyed
1484
+ reconcile like a framework&apos;s list diff, but you spell the key out. Add, remove, and
1485
+ reorder reuse the matching node (preserving focus and scroll) instead of rebuilding the list.
1486
+ Key by a **stable id** on the item; keying by the array index reuses the wrong
1487
+ node on reorder, and `nifra check` flags it as `NF-C022`.
1488
+
1489
+ ```ts
1490
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1491
+ import { signal, bindList } from "@nifrajs/web/nano"
1492
+
1493
+ interface Todo { id: string; text: string; done: boolean }
1494
+ const todos = signal<Todo[]>([])
1495
+
1496
+ // bindList keeps a container's children in sync with an array by KEY - a keyed reconcile, like a
1497
+ // framework's list diff, but explicit. Add/remove/reorder reuse the right node instead of rebuilding.
1498
+ const off = bindList(todos, document.querySelector("ul")!, {
1499
+ key: (t) => t.id, // a STABLE id on the item. Keying by the array index is NF-C022 - reorder breaks.
1500
+ create: (t) => { const li = document.createElement("li"); li.dataset.id = t.id; return li },
1501
+ update: (li, t) => { li.textContent = t.text; li.classList.toggle("done", t.done) },
1502
+ })
1503
+
1504
+ todos.set([...todos.get(), { id: crypto.randomUUID(), text: "buy milk", done: false }])
1505
+ // ...on teardown: off()
1506
+ ```
1507
+
1508
+ ### Async state with resource
1509
+
1510
+ `resource(fetcher, [deps])` is nano&apos;s answer to &ldquo;suspense&rdquo; - an
1511
+ async cell whose value is an explicit `&#123; status, value, error &#125;` union.
1512
+ It runs the fetcher immediately and again whenever a declared dep changes, and it handles the
1513
+ two things people get wrong by hand: the fetcher receives an `AbortSignal`, and a
1514
+ superseded request is aborted with its late result dropped, so the newest fetch always wins -
1515
+ no stale-response flicker. There is no thrown promise and no magic boundary; it is a value you
1516
+ read or bind like any other.
1517
+
1518
+ ```ts
1519
+ import { signal, resource } from "@nifrajs/web/nano"
1520
+
1521
+ const userId = signal(1)
1522
+
1523
+ // resource(fetcher, [deps]) is an async cell: it fetches immediately and again whenever a declared
1524
+ // dep changes. The fetcher gets an AbortSignal; a superseded fetch is aborted and its late result
1525
+ // dropped, so the newest request always wins - no stale-response flicker to hand-guard.
1526
+ const user = resource(async (sig) => {
1527
+ const res = await fetch(`/api/users/${userId.get()}`, { signal: sig })
1528
+ return (await res.json()) as { name: string }
1529
+ }, [userId])
1530
+
1531
+ // The value is an explicit union - status is "pending" | "error" | "ready", never a secret undefined.
1532
+ const off = user.subscribe((s) => {
1533
+ if (s.status === "ready") console.log("got", s.value.name)
1534
+ })
1535
+ userId.set(2) // refetches; the deps array is what NF-C023 checks against the fetcher's .get() reads
1536
+ // ...on teardown: off()
1537
+ ```
1538
+
1539
+ The deps array is checked exactly like `computed`: a `resource` whose
1540
+ fetcher reads a signal the array omits won&apos;t refetch when it changes, and
1541
+ `nifra check` flags it as `NF-C023`. Bind it to the DOM with
1542
+ `bindResource`, which dispatches on `status` - one handler per state,
1543
+ `ready` required, so a still-loading value can never render as a stray
1544
+ `undefined`.
1545
+
1546
+ ```ts
1547
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1548
+ import { signal, resource, bindResource } from "@nifrajs/web/nano"
1549
+
1550
+ const userId = signal(1)
1551
+ const user = resource(async (sig) => {
1552
+ const res = await fetch(`/api/users/${userId.get()}`, { signal: sig })
1553
+ return (await res.json()) as { name: string }
1554
+ }, [userId])
1555
+
1556
+ // bindResource dispatches on status - one handler per state, ready required. Like bind it returns a
1557
+ // disposer (a bare bindResource(...) that drops it is NF-C021).
1558
+ const off = bindResource(document.querySelector("[data-user]")!, user, {
1559
+ pending: (el) => { el.textContent = "Loading…" },
1560
+ ready: (el, u) => { el.textContent = u.name },
1561
+ error: (el) => { el.textContent = "Could not load user" },
1562
+ })
1563
+ // ...on teardown: off()
1564
+ ```
1565
+
1566
+ ### nano inside an island
1567
+
1568
+ nano owns state and DOM edges; an [island](/docs/islands) owns mount and teardown.
1569
+ Put the signals in the enhancer, collect every disposer, and return one cleanup that calls
1570
+ them all. Scaffold this shape with
1571
+ `nifra_scaffold &#123; path, variant: "stateful" &#125;` on a vanilla
1572
+ project - it emits the golden pattern below.
1573
+
1574
+ ```ts
1575
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1576
+ import { defineIsland, mountIslands } from "@nifrajs/web/islands"
1577
+ import { signal, computed, bind, bindList } from "@nifrajs/web/nano"
1578
+
1579
+ interface Todo { id: string; text: string; done: boolean }
1580
+
1581
+ // nano lives INSIDE an island - the island owns mount/teardown, nano owns the state and the DOM edges.
1582
+ const todos = defineIsland<{ items: Todo[] }>((el, props) => {
1583
+ const items = signal<Todo[]>(props.items)
1584
+ const remaining = computed(() => items.get().filter((t) => !t.done).length, [items])
1585
+
1586
+ // Collect every disposer; the island's cleanup calls them all (this is what NF-C021 protects).
1587
+ const cleanups: Array<() => void> = []
1588
+ cleanups.push(bind(el.querySelector("[data-count]")!, remaining, (n, v) => {
1589
+ n.textContent = String(v) + " left"
1590
+ }))
1591
+ cleanups.push(bindList(items, el.querySelector("[data-list]")!, {
1592
+ key: (t) => t.id,
1593
+ create: (t) => { const li = document.createElement("li"); li.dataset.id = t.id; return li },
1594
+ update: (li, t) => { li.textContent = t.text; li.classList.toggle("done", t.done) },
1595
+ }))
1596
+
1597
+ const add = (text: string) =>
1598
+ items.set([...items.get(), { id: crypto.randomUUID(), text, done: false }])
1599
+ void add
1600
+
1601
+ return () => { for (const off of cleanups) off() }
1602
+ })
1603
+
1604
+ mountIslands({ todos })
1605
+ ```
1606
+
1607
+ ### The three checks
1608
+
1609
+ nano&apos;s explicitness is the whole point: because each reactive edge is a visible call, its
1610
+ three mistakes are static lints with a fix recipe - the same closed loop (`scaffold`
1611
+ &rarr; `check` &rarr; `fix` &rarr; `verify`) that makes the
1612
+ backend AI-safe, now on the frontend.
1613
+
1614
+ -
1615
+ `NF-C021` - a `bind`/`bindList` whose disposer is
1616
+ discarded. Collect it; call it on teardown.
1617
+
1618
+ -
1619
+ `NF-C022` - a `bindList` keyed by the array index. Key by a stable id.
1620
+
1621
+ -
1622
+ `NF-C023` - a `computed` that reads a signal its deps omit. Add it to
1623
+ the deps array.
1624
+
1625
+ All three _warn_ - a false positive must never fail a build - and each carries a fix
1626
+ recipe an agent can apply. Where nano stops (client routing, nested views, suspense), stop
1627
+ adding nano and reach for `@nifrajs/web-preact` or another framework adapter.
1628
+
1228
1629
  ## Nifra - Streaming
1229
1630
 
1230
1631
  > Streaming SSR, Suspense, and defer() in Nifra - on every runtime including the edge.
@@ -2967,6 +3368,7 @@ const image = createImageHandler({
2967
3368
  root: "./public", // local sources resolve under here (traversal+symlink guarded)
2968
3369
  allowedOrigins: ["https://cdn.example"], // remote sources: allowlist only (omit ⇒ none)
2969
3370
  // maxWidth, maxSourceBytes, maxSourcePixels, concurrency, cacheMaxAge - all tunable
3371
+ // immutable: true, // only for content-versioned source URLs
2970
3372
  })
2971
3373
 
2972
3374
  // mount it in your router:
@@ -2989,9 +3391,10 @@ probe), and a concurrency semaphore bounding the CPU-heavy codec work.
2989
3391
 
2990
3392
  -
2991
3393
  **Correct + cacheable.** Never upscales past the intrinsic width; negotiates WebP via
2992
- `Accept` (with `Vary: Accept`); serves
2993
- `Cache-Control: …, immutable` + a strong `ETag` computed _before_ any
2994
- decode, so a conditional `If-None-Match` short-circuits the whole pipeline.
3394
+ `Accept` (with `Vary: Accept`); and hashes the emitted bytes for a strong
3395
+ `ETag`, so a changed source at the same URL cannot receive a false `304`.
3396
+ The mutable-safe default is a one-hour `max-age`; opt into `immutable` only
3397
+ when every source URL is content-versioned.
2995
3398
 
2996
3399
  The handler reads the filesystem, so its _local-source_ path targets **Node/Bun servers**
2997
3400
  - but the codec itself is a pluggable `ImageBackend`, so it runs anywhere (see below).
@@ -3819,6 +4222,48 @@ export const vitePlugins = [react()] // dev HMR (Fast Refresh)
3819
4222
  // define = { __VUE_OPTIONS_API__: "true", ... } // Vue feature flags
3820
4223
  ```
3821
4224
 
4225
+ ### Tailwind to StyleX
4226
+
4227
+ Nifra includes a conservative codemod for moving static Tailwind classes to StyleX. It is a dry
4228
+ run by default, so inspect the proposed edits first:
4229
+
4230
+ Supported static `className` attributes become `stylex.props(...)` calls
4231
+ backed by a local `stylex.create(...)` table. Responsive breakpoints and
4232
+ element-local pseudo-classes such as `hover:` are supported. Dynamic class
4233
+ expressions, arbitrary values, parent-dependent variants, and unknown utilities stay unchanged
4234
+ and are reported with file/line evidence. A non-zero exit status means manual migration issues
4235
+ remain.
4236
+
4237
+ StyleX compilation is native to Nifra&apos;s Bun pipeline. Install the runtime and compiler peers,
4238
+ then register both the browser and SSR transforms:
4239
+
4240
+ ```ts
4241
+ # Install the runtime and the optional compiler peers.
4242
+ bun add @stylexjs/stylex
4243
+ bun add -d @babel/core @stylexjs/babel-plugin @babel/plugin-syntax-flow \
4244
+ @babel/plugin-syntax-jsx @babel/plugin-syntax-typescript
4245
+ ```
4246
+
4247
+ ```ts
4248
+ // nifra.config.ts - Bun's client and SSR build transforms.
4249
+ import { stylexBunPlugin } from "@nifrajs/web/plugins/stylex"
4250
+
4251
+ export const clientPlugins = [stylexBunPlugin("dom")]
4252
+ export const serverPlugins = [stylexBunPlugin("ssr")]
4253
+ ```
4254
+
4255
+ If the project intentionally uses Vite, add the Vite adapter alongside its existing Vite
4256
+ transforms. Nifra will use Vite when `vitePlugins` are the app&apos;s only transforms:
4257
+
4258
+ ```ts
4259
+ // doc-check: skip - requires the third-party Vite React plugin
4260
+ // If the app intentionally uses Vite, keep StyleX in vitePlugins.
4261
+ import react from "@vitejs/plugin-react"
4262
+ import { stylexVite } from "@nifrajs/web/plugins/stylex"
4263
+
4264
+ export const vitePlugins = [react(), stylexVite()]
4265
+ ```
4266
+
3822
4267
  ### Scope
3823
4268
 
3824
4269
  `nifra build` defaults to a self-hosted Bun server. Use `--target node`,
@@ -3828,7 +4273,7 @@ the command printed by the build.
3828
4273
 
3829
4274
  ## Nifra - Coding agents
3830
4275
 
3831
- > An MCP server that lets an agent build a Nifra app and then prove it: verified docs and examples, a drift gate to fix against, real requests to check behaviour, and a ladder that says what the project actually holds.
4276
+ > Nifra's agent layer: live MCP tools, bounded repairable turns, native approvals, resumable evidence, content-free browser views, capability registry, decision inbox, Run Studio, A2A, and AG-UI.
3832
4277
 
3833
4278
  Nifra ships an MCP server. It is not a documentation lookup bolted onto a framework: an agent
3834
4279
  can read the project, learn the real API, run actual requests against the backend it just
@@ -3912,6 +4357,8 @@ nifra_check # typecheck + lints, each with a structured fix
3912
4357
  nifra_fix # applies the mechanical ones
3913
4358
  nifra_run {request} # a real request through the backend: status, headers, parsed body
3914
4359
  nifra_render {path} # SSR a page route, returns the HTML
4360
+ nifra_inspect {port} # recent dev-server request traces: status, duration, ISR hit/miss
4361
+ nifra_explain {error} # turn a failure into a structured diagnostic and fix
3915
4362
  nifra_test # bun test, bounded structured results
3916
4363
 
3917
4364
  # 4. What does it now prove?
@@ -3993,8 +4440,8 @@ nifra levels --min 1
3993
4440
 
3994
4441
  ### Building agent features, not just serving them
3995
4442
 
3996
- The tools above let an agent work on your app. Three packages are for the opposite case, where
3997
- the app you are building is itself an AI feature.
4443
+ The tools above let an agent work on your app. Nifra also provides provider-neutral building
4444
+ blocks for the opposite case, where the app you are building is itself an AI feature.
3998
4445
 
3999
4446
  **
4000
4447
  `@nifrajs/prompt`
@@ -4036,6 +4483,139 @@ serves a SQLite database as its own MCP server, fail-closed: allowlisted schema
4036
4483
  default, and read-only queries only when you opt in, with plan verification. Handing a model a
4037
4484
  database connection is not the same as handing it a query tool, and this is the second one.
4038
4485
 
4486
+ ### Run a coding agent
4487
+
4488
+ `@nifrajs/agent-protocol` is the small versioned session and event contract.
4489
+ `@nifrajs/coding-agent` provides the standalone `nifra-agent` host with
4490
+ bounded sessions, checkpoints, workflows, extensions, verification, and authenticated local
4491
+ RPC. `@nifrajs/pi` is an optional Pi backend; other backends implement the same
4492
+ protocol port.
4493
+
4494
+ ```ts
4495
+ bun add @nifrajs/coding-agent @nifrajs/pi
4496
+
4497
+ # Interactive, one-shot, JSON, and local RPC modes are available.
4498
+ bunx nifra-agent --backend pi
4499
+ bunx nifra-agent --backend pi --message "run the checks and explain failures"
4500
+ ```
4501
+
4502
+ The public host keeps live queues and captured evidence bounded, filters verification process
4503
+ environments, and treats extensions as an explicit opt-in. Post-turn `check`,
4504
+ `assure`, or `test` gates emit `verification.completed` and
4505
+ `repair.required`; when enabled, the host sends a bounded repair prompt and reruns
4506
+ the gate until it passes or the attempt cap is reached. The local process adapter and extension
4507
+ worker are crash-containment helpers, not hostile-code sandboxes.
4508
+
4509
+ ```ts
4510
+ # Post-turn gates are opt-in. A failed gate becomes bounded repair work.
4511
+ bunx nifra-agent --backend pi --message "implement the change" \
4512
+ --verify-after-turn check,assure --max-repair-attempts 2
4513
+
4514
+ # Set --max-repair-attempts 0 for observe-only verification.
4515
+ ```
4516
+
4517
+ ### Native approval protocol
4518
+
4519
+ `@nifrajs/coding-agent` can pause a native tool call at the decision boundary and
4520
+ emit `approval.required` with an opaque approval id, action, capability, and bounded
4521
+ coordinates. Resolve it through the host or RPC with `approval.resolve`; the backend
4522
+ emits `approval.resolved` and only then executes the tool. Denial, cancellation,
4523
+ timeout, and closed sessions all fail closed, and `approvalTimeoutMs` bounds how long
4524
+ a pending decision can remain open.
4525
+
4526
+ ### Expose the same agent to other clients
4527
+
4528
+ `@nifrajs/a2a` mounts an A2A 1.0 card and JSON-RPC/SSE binding.
4529
+ `@nifrajs/ag-ui` mounts the AG-UI event stream. Both drive the same bounded runner,
4530
+ compose step evidence with telemetry, and support typed human-in-the-loop continuation. Model
4531
+ ports, state stores, capabilities, and approval transports are injected per request.
4532
+
4533
+ ```ts
4534
+ import { server } from "@nifrajs/core"
4535
+ import { mountA2A } from "@nifrajs/a2a"
4536
+ import { mountAgUI } from "@nifrajs/ag-ui"
4537
+ import type { AgentPorts } from "@nifrajs/agent"
4538
+ import { agent } from "./agent"
4539
+
4540
+ declare const myModelPort: AgentPorts["model"]
4541
+ declare const myStateStore: NonNullable<AgentPorts["state"]>
4542
+
4543
+ const app = server()
4544
+ const ports = () => ({
4545
+ model: myModelPort,
4546
+ capabilities: ["search.read"],
4547
+ state: myStateStore, // scope this to the request subject
4548
+ })
4549
+
4550
+ mountA2A(app, {
4551
+ agent,
4552
+ card: { url: "https://api.example.com/a2a", version: "1.0.0" },
4553
+ ports,
4554
+ })
4555
+ mountAgUI(app, { agent, ports })
4556
+
4557
+ // A2A: GET the card, POST JSON-RPC, optional SSE streaming.
4558
+ // AG-UI: POST RunAgentInput, receive the event stream over SSE.
4559
+ ```
4560
+
4561
+ These mounts deliberately do not authenticate callers: put the route behind your application's
4562
+ auth and authorization middleware, and scope every injected port to the request subject.
4563
+
4564
+ ### Browser views and the Workbench
4565
+
4566
+ `@nifrajs/agent-app` is a backend-free browser SDK for negotiated commands, ordered
4567
+ and resumable event streams, approvals, handoffs, and Run Studio projections. It negotiates
4568
+ optional features including `approvals`, `checkpoint`,
4569
+ `fork`, `handoff`, `inbox`, `reload`,
4570
+ `resume`, and `workflows`, so a browser can degrade deliberately when a
4571
+ host does not offer one. Its public views contain structure - statuses, counters, coordinates,
4572
+ and error codes - rather than prompts, tool payloads, model output, or filesystem paths. The
4573
+ optional Workbench uses that same surface: run
4574
+ `bun run --filter '@nifrajs/workbench' dev -- --cwd /path/to/project` for the local
4575
+ browser client.
4576
+
4577
+ ```ts
4578
+ import { AgentAppClient, HttpAgentTransport } from "@nifrajs/agent-app"
4579
+
4580
+ declare function currentToken(): string
4581
+ declare function render(view: unknown): void
4582
+
4583
+ const client = new AgentAppClient(new HttpAgentTransport({
4584
+ endpoint: "http://127.0.0.1:8787",
4585
+ authorize: () => currentToken(), // minted per request; never stored by the transport
4586
+ }))
4587
+
4588
+ const session = await client.createSession()
4589
+ for await (const view of client.send("summarize the diff")) {
4590
+ render(view) // content-free status, counts, coordinates, and error codes
4591
+ }
4592
+ ```
4593
+
4594
+ ### Capability registry, decision inbox, and Run Studio
4595
+
4596
+ The Workbench's `registry.list` view shows content-free identity cards for admitted
4597
+ capabilities: kind, name, version, schema digest, required capability tokens, and approval,
4598
+ retry, idempotency, and isolation classes. It never renders a prompt, input schema, tool
4599
+ payload, model output, or filesystem path.
4600
+
4601
+ The `inbox` feature lists pending approval and handoff boundaries by exact structural
4602
+ coordinate - run, node, capability, child vector, request id, and expiry. Every approve, deny,
4603
+ assign, resolve, or cancel command carries that coordinate, so stale, mismatched, or expired
4604
+ decisions are refused by the host. `run.studio` projects the same bounded evidence
4605
+ into a run graph, retry/recovery timeline, eval comparison, and deterministic fault-injection
4606
+ view. These are presentation-safe views: content stays in the host.
4607
+
4608
+ ### Evidence, streaming, and telemetry
4609
+
4610
+ `@nifrajs/agent` runs bounded typed turns with tool contracts, budgets, approvals,
4611
+ resumable token-only evidence, model deltas, and a transient shared-state channel. Add an
4612
+ `evidenceLog` to the HTTP seams for SSE replay after a dropped connection; replay
4613
+ resumes evidence and never re-executes the turn. The additive run-lifecycle contract supplies
4614
+ content-free snapshots, ordered evidence refs, feature negotiation, handoff state, and cursor
4615
+ resume. `@nifrajs/agent-telemetry` converts the same constrained evidence into
4616
+ fail-open OpenTelemetry run and step spans. Providers, credentials, durable storage, retention,
4617
+ and tenant policy remain application-owned ports.
4618
+
4039
4619
  ### Projects without a web config
4040
4620
 
4041
4621
  A backend-only project - the shape `create-nifra`'s default template produces - has
@@ -7457,21 +8037,41 @@ isolated fixtures and a test database. Never point a contract laboratory at prod
7457
8037
 
7458
8038
  ## Nifra - Types-first architecture
7459
8039
 
7460
- > One schema is the single source of truth: it drives runtime validation, inferred TypeScript types, the no-codegen typed client, an OpenAPI document, and the MCP contract your agents read.
8040
+ > Nifra infers route types from path literals and handlers automatically, with optional Standard Schemas for validation, explicit contracts, OpenAPI, and agent-readable APIs.
8041
+
8042
+ Nifra starts with automatic inference. A route's path literal, handler context, and return
8043
+ value produce its TypeScript surface; add a Standard Schema when you need runtime validation,
8044
+ coercion, or an explicit request/response contract. The same server type then drives the
8045
+ no-codegen client and the agent-readable API surface.
8046
+
8047
+ ### Inline inference first
8048
+
8049
+ The chainable builder is fully type-inferred. Path parameters are parsed from the route
8050
+ pattern, handler context is typed automatically, and plain return values become the success
8051
+ response seen by the client. This is the quick-start style, similar to Elysia's inline route
8052
+ inference.
7461
8053
 
7462
- In Nifra a single schema is the **source of truth**. The same definition drives runtime
7463
- validation, inferred TypeScript types, the no-codegen typed client, an OpenAPI document, and
7464
- the contract your coding agents read - so the five never drift apart.
8054
+ ```ts
8055
+ import { server } from "@nifrajs/core/server"
8056
+
8057
+ export const app = server()
8058
+ .get("/users/:id", (c) => ({ id: c.params.id, name: "Ada" }))
8059
+ // c.params.id is inferred from the path; the return type becomes the client response.
8060
+ .post("/users", () => ({ created: true }))
8061
+
8062
+ // No schema, annotation, or codegen is required for the route types.
8063
+ ```
7465
8064
 
7466
- ### One schema
8065
+ ### Schemas at the trust boundary
7467
8066
 
7468
- Define request inputs and the response shape once with `t`. Nothing here is
7469
- framework-specific; it's a plain object you attach to a route.
8067
+ Attach a Standard Schema when inputs must be validated before the handler runs, or when you
8068
+ want an explicit response contract. Path params are already inferred from `:id`;
8069
+ the `params` schema below adds runtime constraints/coercion as well.
7470
8070
 
7471
8071
  ```ts
7472
8072
  import { t } from "@nifrajs/schema"
7473
8073
 
7474
- // One contract, defined once. Everything below is derived from it.
8074
+ // Add a schema when the boundary needs runtime validation or a declared contract.
7475
8075
  export const GetUser = {
7476
8076
  params: t.object({ id: t.string() }),
7477
8077
  response: t.object({
@@ -7501,9 +8101,10 @@ export const app = server().get("/users/:id", GetUser, (c) => {
7501
8101
 
7502
8102
  ### Inferred types
7503
8103
 
7504
- The same schema types the handler: `c.params.id` is `string`, and the
7505
- return value is checked against `response`. Change the schema and the handler
7506
- stops compiling until it matches - types and validation can't disagree.
8104
+ Without a schema, `c.params.id` is still `string` and the handler return
8105
+ is captured automatically. With a schema, its output types the handler and its declared
8106
+ `response` constrains what the handler may return. Change either the route or the
8107
+ contract and the typed client follows at compile time.
7507
8108
 
7508
8109
  ### The typed client
7509
8110
 
@@ -7525,6 +8126,16 @@ if (res.ok) {
7525
8126
  }
7526
8127
  ```
7527
8128
 
8129
+ ### Contract-first when the surface must be separate
8130
+
8131
+ Inline inference is the default. If the API needs to be shared, versioned, or implemented by
8132
+ more than one service, declare it with `defineContract` and connect handlers with
8133
+ `implement`. That keeps the contract type available without importing a server
8134
+ implementation into the client.
8135
+
8136
+ See [Framework contract](/docs/contract) for the decoupled form and its scaling
8137
+ guidance.
8138
+
7528
8139
  ### OpenAPI
7529
8140
 
7530
8141
  The `openapi()` middleware builds an OpenAPI 3.1 document from your registered
@@ -7674,6 +8285,222 @@ level that regressed comes back with the reason it broke.
7674
8285
 
7675
8286
  # Package reference (READMEs)
7676
8287
 
8288
+ ## @nifrajs/a2a
8289
+
8290
+ Mount a nifra agent as an [Agent2Agent (A2A)](https://a2a-protocol.org) protocol server. The agent card is served on GET, the A2A 1.0 JSON-RPC binding on POST: `SendMessage` runs one bounded agent turn, `SendStreamingMessage` streams step evidence over SSE, `GetTask` projects the stored turn state.
8291
+
8292
+ A protocol bridge over [`@nifrajs/agent`](../agent) - the request body reuses core's single bounded, prototype-guarded trust boundary, and the model, durable state store, and approval transport are injected per request through the same `ports` factory `mountAgent` uses. Dependency-free beyond the two nifra peers.
8293
+
8294
+ ## Install
8295
+
8296
+ ```bash
8297
+ bun add @nifrajs/a2a @nifrajs/agent
8298
+ ```
8299
+
8300
+ ## Mount
8301
+
8302
+ ```ts
8303
+ import { server } from "@nifrajs/core"
8304
+ import { mountA2A } from "@nifrajs/a2a"
8305
+ import { agent } from "./agent" // an AgentDefinition
8306
+
8307
+ const app = server()
8308
+ mountA2A(app, {
8309
+ agent,
8310
+ card: { url: "https://api.example.com/a2a", version: "1.0.0" },
8311
+ ports: (c) => ({
8312
+ model: myModelPort, // scope to the request subject
8313
+ capabilities: ["search.read"],
8314
+ state: myStateStore, // required for GetTask and resume
8315
+ }),
8316
+ })
8317
+ // GET /.well-known/agent-card.json - the agent card
8318
+ // POST /a2a - SendMessage | SendStreamingMessage | GetTask
8319
+ ```
8320
+
8321
+ ## Task mapping
8322
+
8323
+ One A2A task is one agent turn: `taskId` is the runner's `turnId`.
8324
+
8325
+ | Run result | Task state |
8326
+ | --- | --- |
8327
+ | completed with output | `TASK_STATE_COMPLETED` + an `output` artifact |
8328
+ | completed with a typed error | `TASK_STATE_FAILED` (error in `metadata`) |
8329
+ | suspended (approval, budget, model, max turns) | `TASK_STATE_INPUT_REQUIRED` |
8330
+ | suspended (cancelled) | `TASK_STATE_CANCELED` |
8331
+
8332
+ Agent input is `message.metadata.input` when present, otherwise the first `text` part. Structured output returns as a `text` part carrying JSON.
8333
+
8334
+ ## Human-in-the-loop resume
8335
+
8336
+ A suspended task's status message carries the pending continuation in `metadata`. Send it back to continue the same task - the runtime keeps state token-only, so a suspended tool's input must be replayed in `continuation.input`:
8337
+
8338
+ ```jsonc
8339
+ {
8340
+ "jsonrpc": "2.0", "id": 2, "method": "SendMessage",
8341
+ "params": {
8342
+ "message": {
8343
+ "messageId": "m2", "role": "ROLE_USER", "parts": [],
8344
+ "taskId": "<task id from the suspension>",
8345
+ "metadata": {
8346
+ "input": { "prompt": "go" },
8347
+ "resume": {
8348
+ "continuation": { "kind": "approval", "tool": "search.read", "effectId": "…", "input": { "q": "…" } },
8349
+ "approval": { "granted": true }
8350
+ }
8351
+ }
8352
+ }
8353
+ }
8354
+ }
8355
+ ```
8356
+
8357
+ Cross-request registries (cancellation, subscription fan-out, push notifications) are deliberately out of scope for the stateless seam; those spec methods answer `UnsupportedOperationError` (-32004).
8358
+
8359
+ The seam performs no authentication or authorization - wrap it with your app's route guards and scope every port in `ports(c)` to the caller.
8360
+
8361
+ ## Docs
8362
+
8363
+ Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework - one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
8364
+
8365
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8366
+ [`../../llms-full.txt`](../../llms-full.txt).
8367
+
8368
+ ## @nifrajs/ag-ui
8369
+
8370
+ Mount a nifra agent as an [AG-UI](https://docs.ag-ui.com) (Agent-User Interaction protocol) endpoint: one POST accepting `RunAgentInput`, a stream of AG-UI events out over SSE - run lifecycle, step and tool-call events projected from the runner's step evidence, text message events for the output, and a typed continuation for human-in-the-loop resume.
8371
+
8372
+ A protocol bridge over [`@nifrajs/agent`](../agent) - the request body reuses core's single bounded, prototype-guarded trust boundary, and the model, durable state store, and approval transport are injected per request through the same `ports` factory `mountAgent` uses. Dependency-free beyond the two nifra peers.
8373
+
8374
+ ## Install
8375
+
8376
+ ```bash
8377
+ bun add @nifrajs/ag-ui @nifrajs/agent
8378
+ ```
8379
+
8380
+ ## Mount
8381
+
8382
+ ```ts
8383
+ import { server } from "@nifrajs/core"
8384
+ import { mountAgUI } from "@nifrajs/ag-ui"
8385
+ import { agent } from "./agent" // an AgentDefinition
8386
+
8387
+ const app = server()
8388
+ mountAgUI(app, {
8389
+ agent,
8390
+ ports: (c) => ({
8391
+ model: myModelPort, // scope to the request subject
8392
+ capabilities: ["search.read"],
8393
+ state: myStateStore, // required for resume
8394
+ }),
8395
+ })
8396
+ // POST /agui - RunAgentInput in, AG-UI events out (SSE)
8397
+ ```
8398
+
8399
+ ## Event mapping
8400
+
8401
+ | Runner | AG-UI events |
8402
+ | --- | --- |
8403
+ | run starts | `RUN_STARTED`, then `CUSTOM { name: "nifra.turn" }` announcing the turn id |
8404
+ | tool evidence | `TOOL_CALL_START` / `TOOL_CALL_END`, then `TOOL_CALL_RESULT` whose `content` is the token-only outcome `{ "outcome": "committed" \| "failed" \| "denied", "code"? }` |
8405
+ | model, approval, budget, state evidence | `STEP_STARTED` / `STEP_FINISHED` |
8406
+ | model text deltas | live `TEXT_MESSAGE_START` / `_CONTENT` (see streaming below) |
8407
+ | model reasoning deltas | `REASONING_START` / `REASONING_MESSAGE_START` / `_CONTENT` / `_END` / `REASONING_END` |
8408
+ | model tool-args deltas | provisional `TOOL_CALL_START` + `TOOL_CALL_ARGS`, closed by the call's tool evidence |
8409
+ | shared state | `STATE_SNAPSHOT` on the seed and `STATE_DELTA` (RFC 6902 ops) per patch (see below) |
8410
+ | model usage deltas | summed per `(provider, model)` into the `usage: TokenUsage[]` array on `RUN_FINISHED` |
8411
+ | completed output | `TEXT_MESSAGE_START` / `_CONTENT` / `_END` (unless text was streamed), an optional `MESSAGES_SNAPSHOT` (see below), then `RUN_FINISHED` with `result` and `outcome: { "type": "success" }` |
8412
+ | completed with a typed error | `RUN_ERROR` |
8413
+ | suspended | `CUSTOM { name: "nifra.pending" }` with the continuation, then `RUN_FINISHED` with `outcome: { "type": "interrupt", "interrupts": [...] }` |
8414
+
8415
+ Evidence-derived events carry the evidence `timestamp`. The runtime is token-only by design - `TOOL_CALL_RESULT` reports the outcome and error code, never the tool's payload.
8416
+
8417
+ Agent input is `forwardedProps.input` when present, otherwise the content of the last `role: "user"` message.
8418
+
8419
+ ## Token streaming
8420
+
8421
+ A model port that calls the request's optional `onDelta` callback (see `@nifrajs/agent`'s `AgentModelDelta`) streams live:
8422
+
8423
+ - `{ kind: "text", text }` opens a `TEXT_MESSAGE` and streams each chunk as `TEXT_MESSAGE_CONTENT`. When any text was streamed, the terminal text block is suppressed - the streamed text IS the assistant message, so a streaming port must stream all user-visible text. A non-streaming port keeps the single terminal block; nothing changes for it.
8424
+ - `{ kind: "reasoning", text }` streams a `REASONING_*` message.
8425
+ - `{ kind: "tool-args", name?, argsText }` opens a provisional `TOOL_CALL_START` (id `<turnId>:call:<n>`) and streams `TOOL_CALL_ARGS`. The tool evidence that follows closes the same call - `TOOL_CALL_END` plus the token-only `TOOL_CALL_RESULT` - instead of opening a second one.
8426
+ - `{ kind: "usage", provider?, model?, inputTokens?, ... }` never becomes a frame. The counts are summed per `(provider, model)` across the run's model decisions and stamped as the spec `usage: TokenUsage[]` array on the terminal `RUN_FINISHED` (success and interrupt alike); non-finite figures are ignored.
8427
+
8428
+ Each stream closes when a different delta kind starts, when tool evidence lands, or at the end of the run. Deltas are transient observer data: a `Last-Event-ID` replay carries evidence frames and the stored, unsuppressed terminal events only - the stored `RUN_FINISHED` keeps its `usage`.
8429
+
8430
+ ## Shared state
8431
+
8432
+ The `ports` factory receives `(c, run)`; `run.sharedState` is the run's `AgentSharedState` channel and `run.turnId` the resolved turn id. `body.state` seeds the document and is announced with an upfront `STATE_SNAPSHOT`; every `patch` from a port or tool executor streams as `STATE_DELTA` carrying the RFC 6902 ops. A first patch on an unseeded document announces itself as a `STATE_SNAPSHOT` instead of a delta against a base the client never saw. The channel is per-run and transient - nothing in it is persisted.
8433
+
8434
+ ```ts
8435
+ mountAgUI(app, {
8436
+ agent,
8437
+ ports: (c, run) => ({
8438
+ model: myStreamingModelPort(run.sharedState), // patch progress as the run advances
8439
+ capabilities: ["search.read"],
8440
+ }),
8441
+ })
8442
+ ```
8443
+
8444
+ With `emitMessagesSnapshot: true`, a successful run also emits `MESSAGES_SNAPSHOT` - the request's `messages` echoed back with the assistant output message appended - before `RUN_FINISHED`. It is off by default: the snapshot echoes client message payloads, and terminal events persist to the evidence log when one is configured.
8445
+
8446
+ ## Human-in-the-loop resume
8447
+
8448
+ A suspended run finishes with `outcome: { "type": "interrupt", "interrupts": [interrupt] }` where the interrupt is:
8449
+
8450
+ ```jsonc
8451
+ {
8452
+ "id": "<turnId>",
8453
+ "reason": "approval", // approval | budget | model | cancelled | max_turns
8454
+ "toolCallId": "<effectId>", // present for tool suspensions
8455
+ "responseSchema": { /* JSON Schema for the resume payload */ },
8456
+ "metadata": { "turnId": "…", "continuation": { "kind": "approval", "tool": "…", "effectId": "…" } }
8457
+ }
8458
+ ```
8459
+
8460
+ Resume with the AG-UI `resume` array. The runtime keeps state token-only - it holds no interrupt registry - so the payload must echo `metadata.continuation`, with the suspended tool's input replayed in `continuation.input`:
8461
+
8462
+ ```jsonc
8463
+ {
8464
+ "threadId": "thread-1", "runId": "run-2", "messages": [],
8465
+ "forwardedProps": { "input": { "prompt": "go" } },
8466
+ "resume": [{
8467
+ "interruptId": "<interrupt id>",
8468
+ "status": "resolved", // "cancelled" without an approval resumes as a denial
8469
+ "payload": {
8470
+ "continuation": { "kind": "approval", "tool": "search.read", "effectId": "…", "input": { "q": "…" } },
8471
+ "approval": { "granted": true }
8472
+ }
8473
+ }]
8474
+ }
8475
+ ```
8476
+
8477
+ The pre-interrupt form - the same `{ continuation, approval? }` object in `forwardedProps.resume` plus `forwardedProps.turnId` - keeps working. A resume that fails validation is ignored and the POST starts a fresh run.
8478
+
8479
+ ## Resumable streams
8480
+
8481
+ Pass an `evidenceLog` to make a dropped SSE connection resumable. Evidence-derived frames then
8482
+ carry SSE `id: <seq>`; a client reconnects by re-POSTing the same body with a `Last-Event-ID`
8483
+ header and receives the missed events, rejoining a still-running turn live or replaying the stored
8484
+ terminal events - the run is never re-executed.
8485
+
8486
+ ```ts
8487
+ import { createMemoryAgentEvidenceLog } from "@nifrajs/agent/events"
8488
+
8489
+ mountAgUI(app, { agent, ports, evidenceLog: createMemoryAgentEvidenceLog() })
8490
+ ```
8491
+
8492
+ The in-memory log is the single-process dev/test reference; a durable, multi-process log is an
8493
+ adapter implementing the same `AgentEvidenceLog` interface.
8494
+
8495
+ The seam performs no authentication or authorization - wrap it with your app's route guards and scope every port in `ports(c)` to the caller.
8496
+
8497
+ ## Docs
8498
+
8499
+ Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework - one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
8500
+
8501
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8502
+ [`../../llms-full.txt`](../../llms-full.txt).
8503
+
7677
8504
  ## @nifrajs/agent
7678
8505
 
7679
8506
  Provider-neutral, typed, resumable agent turns over Nifra tool contracts.
@@ -7694,6 +8521,42 @@ const result = await runAgent(definition, { value: input }, ports, {
7694
8521
  The public in-memory adapters are for local development and tests. Durable state, provider
7695
8522
  credentials, and operated policy remain adapter concerns.
7696
8523
 
8524
+ Gateway and deployment contracts are deliberately provider-neutral. Retry, fallback, budget,
8525
+ deadline, workspace, and hostile-code isolation claims are admitted only from a host-approved
8526
+ capability report. The local and replay reference profiles are not hostile-code sandboxes, and no
8527
+ public package loads provider credentials or promises durable execution.
8528
+
8529
+ Several telemetry consumers can observe the same run: `combineAgentTelemetry(a, b, ...)` fans step
8530
+ evidence out to every port in order, so an SSE evidence stream and an exporter (for example
8531
+ `@nifrajs/agent-telemetry`'s `traceAgentRun`) compose instead of displacing each other. The HTTP
8532
+ seams (`mountAgent`, `@nifrajs/a2a`, `@nifrajs/ag-ui`) compose their streaming evidence with any
8533
+ telemetry port supplied through `ports`.
8534
+
8535
+ A model port can stream: when the caller wires an `AgentDeltaSink` into `ports.deltas`, every model
8536
+ request carries an optional `onDelta` callback the port may call per chunk - user-visible text,
8537
+ reasoning text, the raw argument text of the tool call being formed, or a `usage` delta reporting
8538
+ the decision's settled token counts (optionally attributed to a provider and model, for observers
8539
+ to sum across the run). Deltas are transient
8540
+ observer data for live UIs (`@nifrajs/ag-ui` projects them onto `TEXT_MESSAGE_*`, `REASONING_*`,
8541
+ and `TOOL_CALL_ARGS` frames): they are never validated, persisted, or replayed, and a failing sink
8542
+ never fails the model step. `combineAgentDeltaSinks(a, b, ...)` fans deltas out the way
8543
+ `combineAgentTelemetry` fans evidence.
8544
+
8545
+ `createAgentSharedState(initial)` is the run's shared UI state channel: a JSON document any port or
8546
+ tool executor patches with RFC 6902 operations (`add`, `replace`, `remove`), with subscribers
8547
+ observing every applied batch. Patches apply atomically - an invalid op rejects the whole batch -
8548
+ and prototype-grafting pointer segments are refused. The document is per-run observer data, not
8549
+ turn state: nothing in it is persisted by the runtime. `@nifrajs/ag-ui` projects the channel onto
8550
+ AG-UI `STATE_SNAPSHOT`/`STATE_DELTA` events.
8551
+
8552
+ SSE streams are resumable: give `mountAgent` (or `@nifrajs/ag-ui`'s `mountAgUI`) an `evidenceLog`
8553
+ and `step` frames carry `id: <seq>`. A client that loses the connection re-POSTs the same `turnId`
8554
+ with a `Last-Event-ID` header and receives the missed evidence, rejoining a still-running turn live
8555
+ or replaying the stored terminal frame - the run is never re-executed.
8556
+ `createMemoryAgentEvidenceLog` (from `@nifrajs/agent/events`) is the single-process dev/test
8557
+ reference; a durable, multi-process log is an adapter implementing the same `AgentEvidenceLog`
8558
+ interface.
8559
+
7697
8560
  Execution policies can be required by a tool contract. `createLocalProcessAdapter` applies cwd and
7698
8561
  environment filtering, timeouts, and cancellation to child processes. The local adapter is NOT a
7699
8562
  security boundary. Without OS-level sandboxing it contains crashes and accidents, not hostile code.
@@ -7701,6 +8564,111 @@ security boundary. Without OS-level sandboxing it contains crashes and accidents
7701
8564
  For the compact agent contract, see [`LLM.md`](./LLM.md). The full machine-readable corpus is
7702
8565
  [`../../llms-full.txt`](../../llms-full.txt).
7703
8566
 
8567
+ ## @nifrajs/agent-app
8568
+
8569
+ A presentation-safe browser SDK for Nifra agent hosts.
8570
+
8571
+ This package depends on **only** `@nifrajs/agent-protocol`. It pulls in no backend, model provider,
8572
+ storage, or UI framework, so it can be bundled into browser-facing code without dragging a private
8573
+ engine or payload content across the boundary. Everything it surfaces upward is a content-free view
8574
+ model: identifiers, lifecycle statuses, counters, and opaque references. A prompt becomes a character
8575
+ count; a tool result becomes an `ok` flag and an error *code*; a compaction becomes before/after token
8576
+ counts.
8577
+
8578
+ ## What it does
8579
+
8580
+ - **Feature negotiation.** On session creation the client intersects the features it can drive with the
8581
+ capabilities the host advertises, and gates the matching operations behind `requireFeature`.
8582
+ - **Caller auth.** An optional `authorize` hook mints a bearer token per request. The token is placed
8583
+ on the outgoing `Authorization` header and never stored on the transport, copied into a result, or
8584
+ written to a log.
8585
+ - **Ordered, deduplicated streaming.** A turn arrives as an ordered stream of `AgentEventView`s.
8586
+ Out-of-order and redelivered frames are reordered and de-duplicated by sequence; a bounded buffer
8587
+ skips an unfilled gap rather than stalling.
8588
+ - **Cursor resume.** A persisted log can be replayed from a cursor. A cursor whose next record was
8589
+ evicted from the retained window asks the caller to resync.
8590
+ - **Approvals and handoffs.** Pending approvals list as identifiers; decisions and handoff outcomes go
8591
+ back through negotiated commands.
8592
+ - **Run Studio views.** Run graphs, retry/recovery timelines, eval comparisons, and fault-injection
8593
+ rows consume only protocol evidence and bounded replay references.
8594
+ - **Escape hatch.** `command()` reaches host-specific surfaces outside the negotiated contract and
8595
+ still returns a bounded `CommandOutcome` - it never throws the credential.
8596
+
8597
+ ## Usage
8598
+
8599
+ ```ts
8600
+ import { AgentAppClient, HttpAgentTransport } from "@nifrajs/agent-app"
8601
+
8602
+ const transport = new HttpAgentTransport({
8603
+ endpoint: "http://127.0.0.1:8787",
8604
+ authorize: () => currentToken(), // per-request; never stored
8605
+ })
8606
+ const client = new AgentAppClient(transport)
8607
+
8608
+ const session = await client.createSession()
8609
+ if (client.supports("resume")) {
8610
+ const replay = await client.resume({ cursor: lastCursor })
8611
+ // replay.entries: { seq, at, type }[] - no payloads
8612
+ }
8613
+
8614
+ for await (const view of client.send("summarize the diff")) {
8615
+ // view is content-free: e.g. { kind: "assistant.delta", seq, chars }
8616
+ }
8617
+ ```
8618
+
8619
+ ## Boundary
8620
+
8621
+ The transport receives the host's full event stream in order to derive counts, but the client's public
8622
+ output is projected to content-free view models. That projection is the seam: an application built on
8623
+ this SDK can render progress and resolve interactions, and cannot reconstruct a prompt, tool payload,
8624
+ model completion, diagnostic report, or filesystem path. The `scripts/check-agent-boundary.ts` gate
8625
+ enforces that this package imports nothing but `@nifrajs/agent-protocol`.
8626
+
8627
+ The SDK is transport-neutral and opt-in: it does not retain payloads, provide identity or tenancy,
8628
+ claim a remote security boundary, or imply durable delivery. Production hosts own authorization,
8629
+ retention, and adapter policy.
8630
+
8631
+ ## License
8632
+
8633
+ MIT
8634
+
8635
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8636
+ [`../../llms-full.txt`](../../llms-full.txt).
8637
+
8638
+ ## @nifrajs/agent-protocol
8639
+
8640
+ Small, backend-neutral contracts for local coding-agent sessions.
8641
+
8642
+ The package contains versioned session snapshots, streamed assistant/tool/approval
8643
+ events, bounded live event streams, and backend interfaces. It has no runtime
8644
+ dependencies and does not import Nifra framework packages, model SDKs, or UI code.
8645
+
8646
+ ```ts
8647
+ import { createAgentEventStream } from "@nifrajs/agent-protocol"
8648
+
8649
+ const events = createAgentEventStream(256)
8650
+ events.push(/* a versioned AgentEvent */)
8651
+ ```
8652
+
8653
+ Use `@nifrajs/pi` for the Pi backend or implement `AgentBackend` for another
8654
+ runtime. The protocol is intentionally small so CLI, desktop, mobile, and CI
8655
+ clients can share it without pulling in the Nifra application framework.
8656
+
8657
+ ## Version 1 compatibility
8658
+
8659
+ Version 1 is additive. Hosts advertise capabilities and clients use only the negotiated intersection;
8660
+ an unsupported command is a stable `feature_unsupported` result. Optional fields are ignored by older
8661
+ peers, while a cursor gap returns `resync_required` instead of silently skipping evidence. A future
8662
+ major requires a recorded semantic incompatibility, dual decoders, cross-version fixtures, and an
8663
+ approved migration plan.
8664
+
8665
+ Run evidence and lifecycle values are structural only: identifiers, statuses, counters, timestamps,
8666
+ digests, and opaque references. Prompts, model output, tool arguments, and filesystem paths do not
8667
+ belong in these public contracts.
8668
+
8669
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8670
+ [`../../llms-full.txt`](../../llms-full.txt).
8671
+
7704
8672
  ## @nifrajs/agent-telemetry
7705
8673
 
7706
8674
  Child-span telemetry for AI tool calls made through Nifra tool and MCP endpoints.
@@ -7719,6 +8687,35 @@ Requests under `/_nifra/tool/*` and requests to `/mcp` receive tool-name, input-
7719
8687
  duration, and status observations. When `@nifrajs/otel` owns the request observation, tool spans are
7720
8688
  attached as children; otherwise the plugin creates a standalone observation. Other routes pass through.
7721
8689
 
8690
+ ## Tracing agent runs
8691
+
8692
+ `traceAgentRun` turns a `@nifrajs/agent` run's step evidence into an OpenTelemetry trace: one
8693
+ `nifra.agent.run` span per run, one child span per evidence item, with tool names, effect ids, error
8694
+ codes, and effect-ledger heads carried as span attributes. Only the runner's constrained evidence
8695
+ fields are exported - prompts, tool inputs, and outputs cannot enter a trace.
8696
+
8697
+ Orchestration correlation is opt-in and cardinality-bounded. Allowed attributes are run, plan, node,
8698
+ attempt, evidence, replay, and trace coordinates plus bounded status/error codes. Content-bearing or
8699
+ unbounded attributes are rejected; the exporter is not a payload retention sink.
8700
+
8701
+ ```ts
8702
+ import { traceAgentRun } from "@nifrajs/agent-telemetry"
8703
+ import { combineAgentTelemetry, runAgent } from "@nifrajs/agent"
8704
+
8705
+ const trace = traceAgentRun({ agent: agent.name, turnId, exporter })
8706
+ const result = await runAgent(agent, input, {
8707
+ ...ports,
8708
+ telemetry: combineAgentTelemetry(ports.telemetry, trace.telemetry),
8709
+ })
8710
+ trace.end(result)
8711
+ ```
8712
+
8713
+ Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model` span;
8714
+ tool, approval, budget, and state evidence are terminal-only and become instant children. Pass
8715
+ `parent` (for example the enclosing request observation's `context`) to nest runs under the HTTP
8716
+ span, or `traceparent` to continue an inbound W3C trace; `trace.context` is the run span's identity,
8717
+ forwardable with `traceHeaders`. Telemetry is fail-open: a throwing exporter never fails the turn.
8718
+
7722
8719
  ## For AI agents
7723
8720
 
7724
8721
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -7949,8 +8946,9 @@ await cache.invalidateTag(`user:${id}`)
7949
8946
 
7950
8947
  ## Stores
7951
8948
 
7952
- The default `MemoryCache` is in-process with lazy expiry, a tag index, and an optional LRU cap
7953
- (`new MemoryCache({ maxEntries: 10_000 })`). For a cache shared across instances, implement `CacheStore`
8949
+ The default `MemoryCache` is in-process with incremental expiry, a tag index, and a 10,000-entry LRU
8950
+ cap. Tune it with `new MemoryCache({ maxEntries: 2_000 })`; `maxEntries: 0` is the explicit unbounded
8951
+ opt-in. For a cache shared across instances, implement `CacheStore`
7954
8952
  (`get` / `set` / `delete` / `invalidateTag` / `clear`) over CF KV, Redis, etc.:
7955
8953
 
7956
8954
  ```ts
@@ -7987,6 +8985,7 @@ nifra start [--port <n>] [--out <dir>] Run the generated Bun server at <out>
7987
8985
  nifra context Print this project's API + page routes + conventions.
7988
8986
  nifra mcp MCP server (stdio) exposing this project to a coding agent.
7989
8987
  nifra check [--json] Gate: typecheck + lints; --json includes fixes/suggestions.
8988
+ nifra migrate --from tailwind --to stylex Dry-run safe static Tailwind → StyleX migration; --write applies it.
7990
8989
  nifra assure [--config <file>] [--json] Gate reflected routes against enforcement evidence.
7991
8990
  nifra capabilities check [--json] Gate effect provenance + the reviewed capability lockfile.
7992
8991
  nifra manifest emit [--sign <key-ref>] Emit the hash-verified route trust artifact after assurance.
@@ -8049,6 +9048,15 @@ For hand-rolled own-API `fetch()` calls, `nifra check` stays conservative: simpl
8049
9048
  that match a statically visible Nifra route get an exact typed-client rewrite diff; dynamic URLs, custom
8050
9049
  headers, bodies, query strings, or ambiguous routes fall back to manual steps.
8051
9050
 
9051
+ ## `nifra migrate --from tailwind --to stylex`
9052
+
9053
+ This command is a dry run by default. It rewrites static `className` attributes whose utilities have a
9054
+ direct StyleX equivalent into `stylex.props(...)` plus a local `stylex.create(...)` table. Use
9055
+ `--write` to apply the safe changes, and `--dir` to scan a project subdirectory. Responsive breakpoints
9056
+ and element-local pseudo-classes are supported; dynamic expressions, arbitrary values, parent-dependent
9057
+ variants, and unknown utilities are left untouched and reported with file and line evidence. A non-zero
9058
+ exit status means manual migration issues remain.
9059
+
8052
9060
  When a library such as Better Auth intentionally owns a mounted prefix outside the Nifra contract,
8053
9061
  declare that prefix in `nifra.check.json` so its relative calls do not keep CI red:
8054
9062
 
@@ -8175,6 +9183,76 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
8175
9183
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
8176
9184
  done-gate, or `nifra mcp` to give the agent live project tools.
8177
9185
 
9186
+ ## @nifrajs/coding-agent
9187
+
9188
+ Standalone, extensible Nifra coding-agent host and CLI.
9189
+
9190
+ The package provides `nifra-agent` in interactive, one-shot, JSON, and local
9191
+ loopback RPC modes. It adds sessions, checkpoints, bounded compaction,
9192
+ transactional TypeScript extensions, self-healing repair stages, workflows,
9193
+ bounded subagents, and Nifra verification descriptors without changing Nifra's
9194
+ framework runtime.
9195
+
9196
+ Extensions can register bounded workflows that are listed and run through the
9197
+ authenticated RPC surface. Capability manifests, project workspace policies,
9198
+ optional isolated-worktree leases, and a pre-activation syntax gate keep those
9199
+ customizations explicit and replaceable.
9200
+
9201
+ ```sh
9202
+ bunx nifra-agent --backend pi
9203
+ bunx nifra-agent --backend pi --message "run the checks and explain failures"
9204
+ bunx nifra-agent --backend pi --verify-after-turn check --max-repair-attempts 2
9205
+ bunx nifra-agent --backend replay --replay ./session-events.jsonl --json
9206
+ ```
9207
+
9208
+ When --verify-after-turn is enabled, a failed gate becomes a bounded repair turn: the host sends
9209
+ the failure diagnostics back to the agent, runs the gate again, and stops on success or after the
9210
+ configured attempt cap. Use --max-repair-attempts 0 for observe-only verification.
9211
+
9212
+ For embedding, use `CodingAgentHost` or `CodingAgentRpcServer`. The host keeps
9213
+ live event queues bounded, filters verification subprocess environments, and
9214
+ requires explicit local RPC authorization. Install this package only when an
9215
+ application wants the agent product.
9216
+
9217
+ RPC failures return actionable bounded error messages by default. For a trusted
9218
+ loopback debugger, set `exposeErrorStacks: true` (or pass
9219
+ `--expose-error-stacks` to `nifra-agent --rpc`) to include bounded exception
9220
+ stacks. This option is rejected for remote binding.
9221
+
9222
+ `IsolatedExtensionWorker` is an opt-in process-backed crash-containment seam for
9223
+ extensions. It is not a hostile-code sandbox; use OS-level isolation before
9224
+ loading code you do not trust.
9225
+
9226
+ `NifraBackend` is a small provider port for a future native backend. It accepts
9227
+ an injected model implementation and bounded tools without importing a provider
9228
+ SDK. `ReplayBackend` and `readReplayEvents` provide deterministic protocol
9229
+ replays for CI and Workbench regression tests.
9230
+
9231
+ ## At-least-once run dispatch
9232
+
9233
+ `@nifrajs/coding-agent/orchestration` also exports an evidence-only run dispatch port and an adapter
9234
+ over `@nifrajs/jobs`. Dispatch is at-least-once: a lease may be delivered again after worker loss or
9235
+ lease expiry. Every logical node attempt receives a stable idempotency key derived from the plan,
9236
+ run, node, and attempt boundary. Effects must provide a matching proof before recovery can settle a
9237
+ completed boundary; an unproven post-effect failure is dead-lettered rather than guessed safe.
9238
+
9239
+ `createMemoryRunDispatchStore()` is disposable single-process test infrastructure. It makes no
9240
+ durability, hosted-worker, retention, tenant-authorization, or exactly-once guarantee. Durable
9241
+ operated adapters implement the exported `RunDispatchStore` and `DurableDispatchAdapter` ports in
9242
+ their own data layer, including authorization, row policy, retention, reconciliation, and worker
9243
+ health. The public package does not store prompts, model output, tool arguments, or job payloads in
9244
+ its dispatch evidence.
9245
+
9246
+ Legacy `FileSessionStore` files remain supported for explicit local compatibility. Use
9247
+ `nifra-agent --migrate-session <id> --migrate-from <legacy-dir> --migrate-to <evidence-dir>` to
9248
+ create a separately validated, evidence-only target. The source is left untouched and the command
9249
+ does not switch an active configuration pointer; see
9250
+ [`docs/agent-platform/protocol-and-session-migration.md`](../../docs/agent-platform/protocol-and-session-migration.md)
9251
+ for rollback and protocol compatibility details.
9252
+
9253
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
9254
+ [`../../llms-full.txt`](../../llms-full.txt).
9255
+
8178
9256
  ## @nifrajs/content
8179
9257
 
8180
9258
  Typed, schema-validated content collections for nifra - Markdown + frontmatter, framework-agnostic.
@@ -8267,6 +9345,27 @@ import { defineChannel, memoryChannelHub } from "@nifrajs/core/channel"
8267
9345
  - **Inline or contract-first.** Write routes inline (types inferred from the
8268
9346
  builder), or `defineContract(...)` + `implement(...)` for a decoupled, versionable
8269
9347
  surface - handlers lift over unchanged.
9348
+
9349
+ ### Scaling route tables
9350
+
9351
+ Fluent chains are the best fit for a small route surface. TypeScript's type-instantiation budget
9352
+ usually becomes the limiting factor around 95-100 chained routes because every call both infers its
9353
+ handler context and re-threads the growing typed registry. The runtime router is not the limit, and
9354
+ the routes remain fully typed when you compose them as short domain groups:
9355
+
9356
+ ```ts
9357
+ const listings = server()
9358
+ .get("/listings", () => ({ ok: true }))
9359
+ .get("/listings/:id", (c) => ({ id: c.params.id }))
9360
+ const agents = server().get("/agents/:id", (c) => ({ id: c.params.id }))
9361
+
9362
+ const app = server().get("/health", () => ({ ok: true })).merge(listings).merge(agents)
9363
+ ```
9364
+
9365
+ Each group's routes keep the middleware and assurance captured when that group was defined. For a
9366
+ contract-owned surface, `defineContract(...)` + `implement(...)` is the other escape hatch: the
9367
+ registry is declared as one object type, so it does not grow one fluent-instantiation level per
9368
+ route. Split groups before the compiler reports `TS2589`, rather than weakening the route types.
8270
9369
  - **Validation at the boundary.** Per-route `body`/`query` is any
8271
9370
  [Standard Schema](https://standardschema.dev) (zod/valibot/arktype, or `@nifrajs/schema`'s
8272
9371
  `t`); invalid input is rejected with a structured `422` before the handler runs.
@@ -8677,6 +9776,126 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
8677
9776
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
8678
9777
  done-gate, or `nifra mcp` to give the agent live project tools.
8679
9778
 
9779
+ ## @nifrajs/graphql
9780
+
9781
+ Mount a GraphQL endpoint on a nifra app. A spec-compliant [GraphQL-over-HTTP](https://graphql.github.io/graphql-over-http/draft/) handler, `graphql-transport-ws` subscriptions over nifra's native WebSocket lane, and your typed nifra context piped straight into resolvers.
9782
+
9783
+ It executes with the `graphql` package's own `parse`/`validate`/`execute`/`subscribe` - no HTTP framework is re-bundled, the request body reuses core's single bounded, prototype-guarded trust boundary, and the query text is never logged.
9784
+
9785
+ ## Install
9786
+
9787
+ ```bash
9788
+ bun add @nifrajs/graphql graphql
9789
+ # subscriptions also need:
9790
+ bun add graphql-ws
9791
+ ```
9792
+
9793
+ `graphql` is a required peer; `graphql-ws` is optional (only for subscriptions).
9794
+
9795
+ ## Queries and mutations
9796
+
9797
+ ```ts
9798
+ import { server } from "@nifrajs/core"
9799
+ import { mountGraphql } from "@nifrajs/graphql"
9800
+ import { buildSchema } from "graphql"
9801
+
9802
+ const schema = buildSchema(`
9803
+ type Query { hello: String }
9804
+ type Mutation { echo(msg: String!): String }
9805
+ `)
9806
+
9807
+ const rootValue = {
9808
+ hello: () => "world",
9809
+ echo: ({ msg }: { msg: string }) => msg,
9810
+ }
9811
+
9812
+ const app = server()
9813
+ mountGraphql(app, { schema, rootValue })
9814
+ // POST /graphql - queries + mutations
9815
+ // GET /graphql - queries only (idempotent, cacheable)
9816
+ ```
9817
+
9818
+ Prefer the handler directly? Mount it yourself:
9819
+
9820
+ ```ts
9821
+ import { respondGraphql } from "@nifrajs/graphql"
9822
+
9823
+ app.post("/graphql", (c) => respondGraphql(c.req, { schema, rootValue }))
9824
+ ```
9825
+
9826
+ ## Context injection
9827
+
9828
+ The one thing a plain third-party mount cannot do: derive the resolver `contextValue` from what your nifra handler already resolved (auth subject, env, params).
9829
+
9830
+ ```ts
9831
+ mountGraphql(app, {
9832
+ schema,
9833
+ context: ({ request, nifra }) => ({
9834
+ user: nifra?.env, // nifra route context, when mounted via mountGraphql
9835
+ ip: request.headers.get("x-forwarded-for"),
9836
+ }),
9837
+ })
9838
+ ```
9839
+
9840
+ Resolvers read it as their third argument.
9841
+
9842
+ ## Subscriptions
9843
+
9844
+ Subscriptions run over the same route via `graphql-transport-ws`. Bring a subscription source - the bundled in-memory `createPubSub` is the reference implementation; swap in a durable bus (Redis, a Durable Object, NATS) that exposes the same `{ publish, subscribe }` shape for multi-instance deploys.
9845
+
9846
+ ```ts
9847
+ import { server } from "@nifrajs/core"
9848
+ import { websocket } from "@nifrajs/core/ws"
9849
+ import { mountGraphql, createPubSub } from "@nifrajs/graphql"
9850
+
9851
+ const pubsub = createPubSub<number>()
9852
+
9853
+ const app = server().use(websocket())
9854
+ mountGraphql(app, {
9855
+ schema,
9856
+ context: () => ({ pubsub }),
9857
+ subscriptions: true,
9858
+ })
9859
+
9860
+ // A resolver:
9861
+ // count: { subscribe: (_, __, ctx) => ctx.pubsub.subscribe("count"), resolve: (n) => n }
9862
+ // Push an event from anywhere:
9863
+ pubsub.publish("count", 1)
9864
+ ```
9865
+
9866
+ The route must negotiate the `graphql-transport-ws` subprotocol at the WebSocket layer for a browser `graphql-ws` client to connect.
9867
+
9868
+ ## Options
9869
+
9870
+ | Option | Default | Purpose |
9871
+ | --- | --- | --- |
9872
+ | `schema` | - | Executable `GraphQLSchema` (required). |
9873
+ | `context` | `{}` | Build the resolver `contextValue` per operation. |
9874
+ | `rootValue` | - | Root value passed to the executor. |
9875
+ | `maxBodyBytes` | `1_000_000` | Request body cap; larger bodies answer 413. |
9876
+ | `protoPoisoning` | `"reject"` | Prototype-pollution policy for the JSON body. |
9877
+ | `allowedOrigins` | reflect `*` | CORS origin allowlist. |
9878
+ | `authorize` | - | Per-request guard; return `false` for 401. |
9879
+ | `enableGet` | `true` | Serve `GET /graphql` (queries only). |
9880
+ | `subscriptions` | `false` | `true` (reuse schema/context) or explicit WS options. |
9881
+ | `legacyJsonResponse` | `false` | Old always-200 `application/json` shape. |
9882
+
9883
+ ## Responses
9884
+
9885
+ By default responses use the `application/graphql-response+json` media type with spec status codes: `200` for an executed result (including field errors in `errors`), `400` for a parse or validation failure, `401` for a rejected `authorize`, `405` for a mutation attempted over GET. Set `legacyJsonResponse: true` for the older always-`200` `application/json` behavior some clients expect.
9886
+
9887
+ ## License
9888
+
9889
+ MIT
9890
+
9891
+ ## For AI agents
9892
+
9893
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
9894
+ one cheap read instead of the whole corpus. For the wider framework: the repo's
9895
+ [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
9896
+ [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
9897
+ done-gate, or `nifra mcp` to give the agent live project tools.
9898
+
8680
9899
  ## @nifrajs/i18n
8681
9900
 
8682
9901
  Framework-agnostic i18n for nifra - locale negotiation + a tiny ICU message formatter on the platform Intl. Dependency-free.
@@ -8917,6 +10136,12 @@ const q = createQueue({ store: new RedisJobStore(redis) })
8917
10136
  Leasing is at-least-once: a leased job is hidden for `leaseMs`; a worker that dies mid-job releases it
8918
10137
  back automatically. Make handlers **idempotent**.
8919
10138
 
10139
+ The queue remains agent-agnostic. `@nifrajs/coding-agent` can place content-free run-dispatch
10140
+ identities in a dedicated `JobStore`, but this package does not import or define agent concepts. A
10141
+ `MemoryJobStore` is disposable single-process storage; production durability, authorization,
10142
+ retention, reconciliation, and worker coordination belong to the caller's operated adapter. No
10143
+ exactly-once delivery guarantee is implied by a lease.
10144
+
8920
10145
  ## Cloudflare Workers
8921
10146
 
8922
10147
  Workers has no long-lived process, so don't call `start()`. Back the queue with a durable store and a
@@ -8976,6 +10201,10 @@ Mount `mcp.fetch` at `POST /mcp`. The package also exposes the JSON-RPC protocol
8976
10201
  layers directly, plus `defineMcpWidget` and the React adapter for tool results that render UI in MCP
8977
10202
  hosts.
8978
10203
 
10204
+ Clients that include `text/event-stream` in `Accept` receive progress notifications as they happen,
10205
+ followed by the final JSON-RPC response in the same stream. An SSE `GET /mcp` opens a cancellable
10206
+ server-message connection; a plain `GET /mcp` remains a health page.
10207
+
8979
10208
  ## For AI agents
8980
10209
 
8981
10210
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -9357,6 +10586,43 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
9357
10586
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
9358
10587
  done-gate, or `nifra mcp` to give the agent live project tools.
9359
10588
 
10589
+ ## @nifrajs/pi
10590
+
10591
+ Lightweight Pi JSONL/RPC adapter for the Nifra agent protocol.
10592
+
10593
+ It starts Pi in headless RPC mode, maps Pi lifecycle, assistant, tool,
10594
+ compaction, extension, and error events into `@nifrajs/agent-protocol`, and
10595
+ supports cancellation, reload, session identity, and bounded event buffering.
10596
+
10597
+ Reload defaults to a process restart with the same Pi session ID. This is the
10598
+ reliable reload primitive in Pi's documented RPC protocol: the transcript stays
10599
+ in Pi's session store while the new process loads changed extensions. The tiny
10600
+ public-API bridge is still loaded for interactive Pi use. Set
10601
+ `reloadCommand: "prompt"` only for an RPC implementation that dispatches slash
10602
+ commands, or `reloadCommand: "rpc"` for one that provides a top-level reload
10603
+ command. Set `enableReloadBridge: false` when supplying a custom bridge.
10604
+
10605
+ Set `enableNifraTools: true` to load the separately packaged Pi extension with
10606
+ `nifra_context`, `nifra_check`, `nifra_assure`, and `nifra_test` tools. The
10607
+ default is off so embedding Pi stays minimal.
10608
+
10609
+ ```ts
10610
+ import { PiBackend } from "@nifrajs/pi"
10611
+
10612
+ const backend = new PiBackend()
10613
+ const session = await backend.createSession({ cwd: process.cwd() })
10614
+ for await (const event of backend.send({ sessionId: session.id, message: "inspect this project" })) {
10615
+ console.log(event.type)
10616
+ }
10617
+ ```
10618
+
10619
+ Pi stays an optional dependency of the agent product. Nothing in this package is
10620
+ reachable from `@nifrajs/core`, `@nifrajs/client`, `@nifrajs/web`, or
10621
+ `@nifrajs/schema`.
10622
+
10623
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
10624
+ [`../../llms-full.txt`](../../llms-full.txt).
10625
+
9360
10626
  ## @nifrajs/prompt
9361
10627
 
9362
10628
  Provider-neutral, type-safe prompts with Standard Schema input and validated structured output.
@@ -9410,7 +10676,7 @@ const upstream = createProxy({ upstream: "http://127.0.0.1:8081" })
9410
10676
  const app = server().mountFetch("/api", upstream, { stripPrefix: true })
9411
10677
  ```
9412
10678
 
9413
- The upstream is a **bare origin** fixed at construction - the forwarded URL is built by mutating a clone of that origin, never by resolving request-derived strings, so no request input can change which host is dialed. Hop-by-hop and `Connection`-nominated headers are stripped in both directions, forwarding metadata (`X-Forwarded-*`) is dropped unless `forwardClientIp: true`, upstream redirects are never followed, and TLS verification has no off switch. Unreachable upstreams answer a flat `502`; the deadline (`timeoutMs`, default 30s) answers `504`.
10679
+ The upstream is a **bare origin** fixed at construction - the forwarded URL is built by mutating a clone of that origin, never by resolving request-derived strings, so no request input can change which host is dialed. Hop-by-hop and `Connection`-nominated headers are stripped in both directions. Caller IP/protocol metadata is dropped unless `forwardClientIp: true`; the inbound Host is never trusted, and `X-Forwarded-Host` is emitted only from a fixed `forwardedHost`. Upstream redirects are never followed, and TLS verification has no off switch. Unreachable upstreams answer a flat `502`; the deadline (`timeoutMs`, default 30s) answers `504`.
9414
10680
 
9415
10681
  ## Docs
9416
10682
 
@@ -9572,6 +10838,11 @@ pi install npm:@nifrajs/skills
9572
10838
  /plugin install nifra@nifra
9573
10839
  ```
9574
10840
 
10841
+ The plugin registers the Nifra MCP server as well as the skills, so a fresh install has the live tools
10842
+ the skills tell it to prefer. Its launch spec is deliberately unpinned - a plugin is installed once and
10843
+ opened against many projects, so the CLI resolves per project and the server reports version skew
10844
+ itself rather than freezing one version across all of them.
10845
+
9575
10846
  **Anything that reads a skills directory** (Cursor, `.agents/skills`, a project `.pi/skills`)
9576
10847
 
9577
10848
  ```sh
@@ -9588,6 +10859,9 @@ against the version installed in the project being edited:
9588
10859
  bunx @nifrajs/cli init-agents # register the MCP server in an existing app
9589
10860
  ```
9590
10861
 
10862
+ The Claude Code plugin brings its own copy of that server, so `init-agents` is for the other surfaces -
10863
+ and for committing a version-pinned `.mcp.json` that every contributor on the project shares.
10864
+
9591
10865
  Not in a Nifra project? The docs tools are hosted at `https://mcp.nifra.dev`.
9592
10866
 
9593
10867
  That split is the anti-drift design: a skill that restated the API would rot on the next release,
@@ -9867,6 +11141,14 @@ that every call carries and captures cookies via a shared jar.
9867
11141
 
9868
11142
  For a **stateless** request (no cookies), use `testClient` from `@nifrajs/client` directly.
9869
11143
 
11144
+ ### Agent evaluation and failure matrix
11145
+
11146
+ `runAgentEvalComposition` composes trajectory replay, fault profiles, contract invariants,
11147
+ idempotency proofs, and adapter certification. Reports expose explicit invariant and rubric cases,
11148
+ stable regression IDs, schedule tokens, and digests; they do not retain prompts, tool payloads,
11149
+ model output, labels, or a single opaque quality score. The failure matrix covers model, tool,
11150
+ approval, cancellation, lease, cursor, registry, and deployment boundaries.
11151
+
9870
11152
  ## For AI agents
9871
11153
 
9872
11154
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -10077,6 +11359,14 @@ Zero-framework render adapter for @nifrajs/web - auto-escaping tagged-template H
10077
11359
 
10078
11360
  Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework - one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
10079
11361
 
11362
+ ## The AI-safe lane
11363
+
11364
+ Vanilla is the render adapter an agent gets right on the first try. It ships no framework runtime, so there is no hydration, no reactivity, and none of the failure modes (stale closures, hydration mismatch, what-re-runs-when) that trip up generated code. You get true SSR plus every server feature - loaders, actions, ISR/SSG, streaming, head management - unchanged from `@nifrajs/web`; those live in the core, not the view layer.
11365
+
11366
+ Interactivity is added with **islands**, not hydration: small imperative DOM enhancers over server-rendered elements. `defineIsland` types the props, `createIslandBus` coordinates islands over a typed channel, and every enhancer returns its cleanup - the one rule `nifra check` enforces (`NF-C020`). See the [islands cookbook](https://nifra.dev/docs/islands) for the counter, cart-badge, and filter patterns, or run `nifra scaffold` on a vanilla route for the golden stub.
11367
+
11368
+ Need a stateful app UI with reactive components? That is what the five framework adapters are for - reach for `@nifrajs/web-preact` or `@nifrajs/web-solid` when you want a small reactive runtime.
11369
+
10080
11370
  ## Install
10081
11371
 
10082
11372
  ```sh
@@ -10237,13 +11527,29 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10237
11527
 
10238
11528
  # Complete export index (generated from each package's src/index.ts)
10239
11529
 
11530
+ ## @nifrajs/a2a
11531
+
11532
+ `A2A_ERROR_CODES`, `A2A_PROTOCOL_VERSION`, `A2AAgentCard`, `A2AAgentSkill`, `A2ACardInfo`, `A2AMountableApp`, `A2ARouteContext`, `A2ATaskState`, `AGENT_CARD_WELL_KNOWN_PATH`, `agentCard`, `mountA2A`, `MountA2AOptions`
11533
+
11534
+ ## @nifrajs/ag-ui
11535
+
11536
+ `AgUIMountableApp`, `AgUIRouteContext`, `AgUIRunContext`, `AgUIRunUsage`, `mountAgUI`, `MountAgUIOptions`
11537
+
10240
11538
  ## @nifrajs/agent
10241
11539
 
10242
- `AgentApprovalPort`, `AgentApprovalResult`, `AgentDefinition`, `AgentModelPort`, `AgentModelRequest`, `AgentModelResponse`, `AgentPendingContinuation`, `AgentPendingKind`, `AgentPorts`, `AgentRunResult`, `AgentStateStore`, `AgentStatus`, `AgentStepEvidence`, `AgentTelemetryPort`, `AgentToolContract`, `AgentToolDescriptor`, `AgentToolResult`, `AgentTranscript`, `AgentTurnBaseResult`, `AgentTurnError`, `AgentTurnInput`, `AgentTurnResult`, `AgentTurnState`, `createAgentState`, `createLocalProcessAdapter`, `ExecutionPolicy`, `ExecutionPolicyAdapter`, `LOCAL_PROCESS_LIMITATION`, `LocalProcessAdapter`, `LocalProcessAdapterOptions`, `LocalProcessPolicyError`, `LocalProcessRequest`, `LocalProcessResult`, `MemoryAgentStateStore`, `replayAgent`, `resumeAgent`, `runAgent`, `RunAgentOptions`, `turn`
11540
+ `AgentApprovalPort`, `AgentApprovalResult`, `AgentDefinition`, `AgentDeltaSink`, `AgentDeployment`, `AgentDeploymentAdapter`, `AgentDeploymentPlan`, `AgentModelDelta`, `AgentModelPort`, `AgentModelRequest`, `AgentModelResponse`, `AgentPendingContinuation`, `AgentPendingKind`, `AgentPorts`, `AgentRunResult`, `AgentSharedState`, `AgentStatePatchOp`, `AgentStateStore`, `AgentStatus`, `AgentStepEvidence`, `AgentTelemetryPort`, `AgentToolContract`, `AgentToolDescriptor`, `AgentToolResult`, `AgentTranscript`, `AgentTurnBaseResult`, `AgentTurnError`, `AgentTurnInput`, `AgentTurnResult`, `AgentTurnState`, `ApprovalClass`, `assertDeploymentAuthorityMonotonic`, `CAPABILITY_DESCRIPTOR_VERSION`, `CapabilityDescriptor`, `CapabilityKind`, `combineAgentDeltaSinks`, `combineAgentTelemetry`, `composeDescriptor`, `composeRegistrySnapshot`, `createAgentSharedState`, `createAgentState`, `createDeploymentAuthority`, `createLocalProcessAdapter`, `createStructuredOutputParser`, `DeploymentActivationOptions`, `DeploymentAuthority`, `DeploymentCancellation`, `DeploymentCancelRequest`, `DeploymentCancelResult`, `DeploymentCapabilities`, `DeploymentCapabilityReport`, `DeploymentDisposeRequest`, `DeploymentDisposeResult`, `DeploymentError`, `DeploymentErrorCode`, `DeploymentEvidence`, `DeploymentFilesystem`, `DeploymentInspection`, `DeploymentInspectRequest`, `DeploymentNetwork`, `DeploymentPrepareRequest`, `DeploymentPrepareResult`, `DeploymentProcess`, `DeploymentRuntime`, `DeploymentSecrets`, `DeploymentStartRequest`, `DeploymentStartResult`, `DeploymentState`, `descriptorFromTool`, `DescriptorInput`, `ExecutionPolicy`, `ExecutionPolicyAdapter`, `FakeModelGateway`, `FakeModelGatewayOptions`, `HostileCodeIsolation`, `IdempotencyClass`, `isModelGatewayErrorCode`, `IsolationClass`, `LOCAL_PROCESS_LIMITATION`, `LocalProcessAdapter`, `LocalProcessAdapterOptions`, `LocalProcessPolicyError`, `LocalProcessRequest`, `LocalProcessResult`, `MemoryAgentStateStore`, `MODEL_GATEWAY_ERROR_CODES`, `ModelGateway`, `ModelGatewayAttemptRequest`, `ModelGatewayBudget`, `ModelGatewayEnvelope`, `ModelGatewayError`, `ModelGatewayErrorCode`, `ModelGatewayEvidence`, `ModelGatewayExecutionResult`, `ModelGatewayFailure`, `ModelGatewayFailureError`, `ModelGatewayRawResult`, `ModelGatewayRequest`, `ModelGatewayResult`, `ModelGatewaySuccess`, `ModelGatewayTerminalFailure`, `ModelGatewayUsage`, `ModelRoute`, `ModelRoutePolicy`, `ModelRouteRef`, `parseCapabilityDescriptor`, `parseDeploymentCapabilityReport`, `parseDeploymentPlan`, `parseModelGatewayError`, `parseModelGatewayResult`, `REGISTRY_SNAPSHOT_VERSION`, `RegistryError`, `RegistryErrorCode`, `RegistrySnapshot`, `replayAgent`, `ReplayModelGateway`, `ReplayModelGatewayOptions`, `resumeAgent`, `RetryClass`, `runAgent`, `RunAgentOptions`, `runModelGateway`, `structuredOutputParser`, `StructuredOutputParser`, `ToolDescriptorOptions`, `turn`
11541
+
11542
+ ## @nifrajs/agent-app
11543
+
11544
+ `AGENT_APP_FEATURES`, `AgentAppClient`, `AgentAppClientOptions`, `AgentAppError`, `AgentEventView`, `AgentTransport`, `AgentTransportError`, `AgentTransportRequest`, `ApprovalRequiredView`, `ApprovalResolvedView`, `AssistantChunkView`, `AuthProvider`, `boundaryCommands`, `BoundaryDecisionResult`, `boundaryIsStale`, `BoundaryItemView`, `BoundaryStateView`, `CommandOutcome`, `CreateSessionInput`, `EvalComparisonView`, `EvidenceTimelineView`, `ExtensionReloadedView`, `FaultInjectionView`, `HandoffView`, `HttpAgentTransport`, `HttpAgentTransportOptions`, `MemoryCompactedView`, `OrderedEventBuffer`, `parseEventStream`, `PendingApprovalView`, `RegistryCapabilityView`, `RepairRequiredView`, `ReplayEntryView`, `ReplayResult`, `ResolveHandoffInput`, `ResumeInput`, `RunStudioNodeState`, `RunStudioNodeView`, `RunStudioView`, `RunView`, `SessionFailedView`, `SessionLifecycleView`, `SessionStoppedView`, `SessionView`, `toEvalComparisonView`, `toEventView`, `toEvidenceTimelineView`, `toFaultInjectionViews`, `toHandoffView`, `ToolCompletedView`, `ToolDeltaView`, `ToolStartedView`, `toRegistryCapabilityView`, `toRunStudioView`, `toRunView`, `toSessionView`, `TurnStartedView`, `VerificationCompletedView`, `virtualizeEvidenceRows`
11545
+
11546
+ ## @nifrajs/agent-protocol
11547
+
11548
+ `AGENT_PROTOCOL_VERSION`, `AgentApprovalRequiredEvent`, `AgentApprovalResolvedEvent`, `AgentAssistantDeltaEvent`, `AgentAssistantMessageEvent`, `AgentBackend`, `AgentBackendInfo`, `agentError`, `AgentError`, `AgentEvent`, `AgentEventPayload`, `AgentEventSink`, `AgentEventStream`, `AgentEventType`, `AgentExtensionReloadedEvent`, `AgentMemoryCompactedEvent`, `AgentRepairRequiredEvent`, `AgentRepairTask`, `AgentSessionCheckpoint`, `AgentSessionCompletedEvent`, `AgentSessionEventBase`, `AgentSessionFailedEvent`, `AgentSessionSnapshot`, `AgentSessionStartedEvent`, `AgentSessionStatus`, `AgentSessionStoppedEvent`, `AgentSessionUpdatedEvent`, `AgentToolCompletedEvent`, `AgentToolDeltaEvent`, `AgentToolStartedEvent`, `AgentTurnStartedEvent`, `AgentVerificationCompletedEvent`, `createAgentEventStream`, `CreateSessionInput`, `ForkSessionInput`, `ForkSessionResult`, `isAgentEvent`, `ReloadResult`, `SendMessageInput`
10243
11549
 
10244
11550
  ## @nifrajs/agent-telemetry
10245
11551
 
10246
- `agentTelemetry`, `AgentTelemetryOptions`, `consoleAgentExporter`
11552
+ `AgentRunEvidence`, `AgentRunOutcome`, `AgentRunTrace`, `agentTelemetry`, `AgentTelemetryOptions`, `consoleAgentExporter`, `orchestrationTelemetry`, `OrchestrationTelemetry`, `OrchestrationTelemetryEvent`, `OrchestrationTelemetryKind`, `OrchestrationTelemetryOptions`, `traceAgentRun`, `TraceAgentRunOptions`
10247
11553
 
10248
11554
  ## @nifrajs/auth
10249
11555
 
@@ -10265,13 +11571,17 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10265
11571
 
10266
11572
  `ActionArgs`, `ActionData`, `ApiError`, `ApiProxy`, `client`, `ClientOptions`, `ClientRetryOptions`, `FetchFn`, `inProcessClient`, `InProcessClient`, `InProcessClientOptions`, `Jsonify`, `LoaderArgs`, `LoaderData`, `RegistryOf`, `RESERVED_EXACT_KEYS`, `RESERVED_KEY_READOUT`, `RESERVED_VERB_KEYS`, `reservedKeyFor`, `ResponseContractViolation`, `Result`, `SubscribeOptions`, `Subscription`, `testClient`, `Treaty`, `TreatyFromRegistry`, `WsCallOptions`, `WsHandle`
10267
11573
 
11574
+ ## @nifrajs/coding-agent
11575
+
11576
+ `AGENT_CAPABILITY_MANIFEST_VERSION`, `AGENT_PRESETS`, `AgentCapability`, `AgentCapabilityManifest`, `AgentPlan`, `AgentPreset`, `AgentPresetName`, `ApprovalDecision`, `ApprovalManager`, `ApprovalManagerOptions`, `ApprovalMatchRejection`, `ApprovalMatchResult`, `ApprovalRequest`, `approvalRequestFromEvent`, `approvalResolvedEvent`, `BoundedSubagentRunner`, `BoundedText`, `CapabilityDescriptor`, `CiDeploymentAdapter`, `CodingAgentExtension`, `CodingAgentHost`, `CodingAgentHostOptions`, `CodingAgentRpcServer`, `CodingAgentRpcServerHandle`, `CodingAgentRpcServerOptions`, `CompactionReport`, `ContextRecord`, `ContextWindow`, `ContextWindowOptions`, `createCapabilityManifest`, `createCiDeploymentAdapter`, `createLocalProcessDeploymentAdapter`, `createNativeGatewayModelPort`, `createNifraTools`, `createPresetSpec`, `createReplayDeploymentAdapter`, `createVerificationRepairTask`, `deniedCapabilities`, `DEPLOYMENT_REFERENCE_PROFILES`, `discoverExtensions`, `ExtensionCommand`, `ExtensionContext`, `extensionDescriptor`, `ExtensionDescriptorOptions`, `extensionDescriptors`, `ExtensionDescriptorSource`, `ExtensionEventHandler`, `ExtensionHost`, `ExtensionHostOptions`, `ExtensionProvider`, `ExtensionReloadResult`, `ExtensionSubagent`, `ExtensionTool`, `FileSessionStore`, `FileSessionStoreOptions`, `getAgentPreset`, `HandoffCoordinator`, `HandoffCoordinatorOptions`, `HandoffDecision`, `HandoffError`, `HandoffRejection`, `HandoffView`, `HealingEvent`, `HealingOptions`, `HealingResult`, `IsolatedExtensionSnapshot`, `IsolatedExtensionTool`, `IsolatedExtensionWorker`, `IsolatedExtensionWorkerOptions`, `LocalProcessDeploymentAdapter`, `migrateLegacySession`, `MigrateLegacySessionOptions`, `NativeApprovalPort`, `NativeGatewayModelPortOptions`, `NativeMessage`, `NativeModelChunk`, `NativeModelPort`, `NativeModelRequest`, `NativeModelResponse`, `NativeTool`, `NIFRA_AGENT_INSTRUCTIONS`, `NifraAgentTool`, `NifraBackend`, `NifraBackendOptions`, `NifraContextOptions`, `NifraContextResult`, `OpenHandoffInput`, `parseCapabilityManifest`, `parseSessionEvidenceRecord`, `PiBackend`, `PiBackendOptions`, `PlanEvent`, `PlanPhase`, `PlanResult`, `PlanRunner`, `PlanRunnerOptions`, `ProjectDiffOptions`, `ProjectDiffResult`, `readBoundedText`, `readProjectDiff`, `readReplayEvents`, `RepairProposal`, `ReplayBackend`, `ReplayBackendOptions`, `ReplayDeploymentAdapter`, `runNifraContext`, `runNifraVerification`, `SelfHealingController`, `SESSION_EVIDENCE_VERSION`, `SessionEvidenceRecord`, `SessionLogEntry`, `SessionMigrationError`, `SessionMigrationReport`, `SessionStore`, `stableSessionEventCode`, `SubagentExecutor`, `SubagentResult`, `SubagentRunnerOptions`, `SubagentSpec`, `UiExtensionHost`, `UiExtensionHostOptions`, `UiExtensionManifest`, `UiExtensionSlot`, `UiReloadResult`, `UiStatusWidget`, `UiThemeDescriptor`, `validateExtensionModule`, `VerificationOptions`, `VerificationRepairTask`, `VerificationResult`, `WorkflowContext`, `WorkflowEvent`, `WorkflowResult`, `WorkflowRunner`, `WorkflowRunnerOptions`, `WorkflowStep`
11577
+
10268
11578
  ## @nifrajs/content
10269
11579
 
10270
11580
  `bakeCollection`, `BakedCollection`, `BakedCollectionIndex`, `BakedCollectionJoin`, `BakedIndexBucket`, `ContentFieldKey`, `Entry`, `fromBaked`, `fromBakedIndex`, `fromBakedJoin`, `indexCollection`, `IndexedCollection`, `IndexedJoin`, `IndexOptions`, `IndexPage`, `IndexQueryOptions`, `IndexRange`, `IndexRanges`, `IndexSort`, `IndexWhere`, `InferSchema`, `JoinCardinality`, `joinCollections`, `JoinedRow`, `parseEntry`, `ParseEntryOptions`, `parseFrontmatter`, `SharedKey`, `StandardSchemaV1`, `StaticCollection`
10271
11581
 
10272
11582
  ## @nifrajs/core
10273
11583
 
10274
- `Version`, `VERSION`
11584
+ `AdmissionController`, `AdmissionDecision`, `AnyServer`, `commonSecretPatterns`, `Context`, `ContextPlugin`, `cookieNamePrefix`, `CookieOptions`, `CookieSecret`, `defineContextPlugin`, `defineIdentityPlugin`, `definePlugin`, `DefinePluginResult`, `defineRouterPlugin`, `DurableObjectNamespaceLike`, `ExecutionContext`, `FetchApp`, `FRAMEWORK_NAME`, `FrameworkError`, `FrameworkName`, `Handler`, `IdentityPlugin`, `InferInput`, `InferOutput`, `isSameOriginPath`, `isSameOriginRequest`, `jsonLogger`, `LambdaEvent`, `LambdaHandler`, `LambdaResponse`, `LambdaV1Event`, `LambdaV2Event`, `LogFields`, `Logger`, `McpPromptDescriptor`, `McpResourceDescriptor`, `Method`, `METHODS`, `Middleware`, `NetlifyEvent`, `NetlifyHandler`, `NifraFeatureVersion`, `NifraPlugin`, `NifraWebSocket`, `NodeRequestContext`, `NodeRequestHook`, `NodeResponseContext`, `NodeResponseHook`, `NodeServeOutcome`, `OnRequestResult`, `Params`, `parseCookies`, `pathnameOf`, `PlainRender`, `Platform`, `PlatformResponse`, `PluginTypeCollapsed`, `Prettify`, `PromptArgument`, `PromptMessage`, `redactLogFields`, `RedactOptions`, `Registry`, `ResponseBodyHook`, `ResponseBodyReplacement`, `ResponseControls`, `ResponseFinalization`, `ResponseHeadersHook`, `ResponseHeadersView`, `ResponseMapFor`, `ResponseResult`, `RouteConfigError`, `RouteConfigErrorCode`, `RouteDescriptor`, `RouteInfo`, `RouteInfoFor`, `Router`, `RouterMatch`, `RouteSchema`, `RunningServer`, `ScheduledController`, `ScheduledHandler`, `serializeCookie`, `server`, `Server`, `ServerOptions`, `signValue`, `silentLogger`, `SSEContext`, `SSEInit`, `SSEMessage`, `SSEStream`, `StandardIssue`, `StandardResult`, `StandardSchemaV1`, `StandardTypes`, `StandardWebSocket`, `status`, `StatusResponse`, `toFetchHandler`, `toLambdaHandler`, `toNetlifyHandler`, `ToolAnnotations`, `toVercelHandler`, `TypedSSEStream`, `unsignValue`, `UrlParts`, `urlPartsOf`, `ValidationOutcome`, `VercelHandler`, `Version`, `VERSION`, `WebSocketContext`, `WebSocketData`, `WebSocketHandler`, `WebSocketUpgradeOutcome`
10275
11585
 
10276
11586
  ## @nifrajs/cron
10277
11587
 
@@ -10297,6 +11607,10 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10297
11607
 
10298
11608
  `createEventRegistry`, `defineEventContract`, `EventContract`, `EventContractError`, `EventEnvelope`, `EventParseResult`, `EventRegistry`, `RegistryParseResult`
10299
11609
 
11610
+ ## @nifrajs/graphql
11611
+
11612
+ `buildContext`, `createPubSub`, `GraphqlContextBuilder`, `GraphqlContextInput`, `GraphqlHttpOptions`, `GraphqlPubSub`, `graphqlWebSocket`, `GraphqlWsOptions`, `mountGraphql`, `MountGraphqlOptions`, `NifraContextLike`, `respondGraphql`
11613
+
10300
11614
  ## @nifrajs/i18n
10301
11615
 
10302
11616
  `createFormatter`, `Formatter`, `Locale`, `LocaleParts`, `LocaleSource`, `Messages`, `negotiateLocale`, `NegotiateOptions`, `ResolvedLocale`, `resolveLocale`
@@ -10341,6 +11655,10 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10341
11655
 
10342
11656
  `ActiveObservation`, `AttributeValue`, `causalitySpanLink`, `combineObservationAdapters`, `consoleSpanExporter`, `createObservationLifecycle`, `effectTracing`, `EffectTracingOptions`, `EffectTracingPlugin`, `EndObservation`, `formatTraceparent`, `generateSpanId`, `generateTraceId`, `NifraSpan`, `ObservationAdapter`, `ObservationClock`, `ObservationContext`, `ObservationLifecycle`, `ObservationLifecycleOptions`, `ObservationLink`, `ObservationParent`, `otlpExporter`, `OtlpExporter`, `OtlpExporterOptions`, `ParsedTraceparent`, `parseTraceparent`, `SpanStatus`, `StartObservation`, `TraceContext`, `traceHeaders`, `tracing`, `TracingOptions`
10343
11657
 
11658
+ ## @nifrajs/pi
11659
+
11660
+ `PiBackend`, `PiBackendOptions`
11661
+
10344
11662
  ## @nifrajs/prompt
10345
11663
 
10346
11664
  `prompt`, `Prompt`, `PromptInputError`, `PromptMessage`, `PromptOutputError`, `PromptRequest`, `PromptResponseFormat`, `RunOptions`
@@ -10363,7 +11681,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10363
11681
 
10364
11682
  ## @nifrajs/testing
10365
11683
 
10366
- `AdapterCertificationError`, `AdapterCertificationProfile`, `AdapterCertificationReport`, `AdversarialContractError`, `AdversarialContractOptions`, `AdversarialContractReport`, `AdversarialContractResult`, `AppLike`, `assertAdapterCertification`, `assertAdversarialContract`, `assertIncidentReplays`, `assertTrajectoryInvariants`, `cacheStoreCertificationProfile`, `CapturedRequest`, `CapturedRequestInput`, `captureIncident`, `CaptureIncidentOptions`, `CertifiableCacheEntry`, `CertifiableCacheStore`, `CertifiableDomainEvent`, `CertifiableEventDeliveryAdapter`, `CertifiableEventRecord`, `CertifiableJobStore`, `CertifiableRuntimeAdapter`, `CertifiableRuntimeServer`, `CertifiableStorageAdapter`, `CertificationCapabilityEvidence`, `CertificationCheck`, `CertificationCheckEvidence`, `certifyAdapter`, `checkTrajectoryInvariants`, `ContractCaseContext`, `ContractCaseKind`, `ContractCoverageGap`, `ContractCoverageGapCode`, `ContractReplay`, `ContractRuntime`, `ContractTarget`, `ContractTestApp`, `ContractWitness`, `cookieJar`, `CookieJar`, `createFailureLab`, `createTrajectoryTranscript`, `CreateTrajectoryTranscriptOptions`, `defineCertificationProfile`, `defineFaultProfile`, `EffectLedger`, `eventDeliveryCertificationProfile`, `FailureDirective`, `FailureEvidence`, `FailureInjectedError`, `FailureKind`, `FailureLab`, `FailureLabOptions`, `FailureReplay`, `FailureScenario`, `FailureScenarioReport`, `FaultProfile`, `FaultProfileFault`, `FaultProfileFaultKind`, `FaultProfileReport`, `FaultProfileScenario`, `FaultProfileScenarioReport`, `generateRegressionTest`, `GenerateRegressionTestOptions`, `httpToolAdapter`, `IdempotencyDivergence`, `IdempotencyProof`, `IncidentCapsule`, `IncidentReplayError`, `IncidentReplayResult`, `inProcessToolAdapter`, `jobStoreCertificationProfile`, `mcpToolAdapter`, `proveIdempotency`, `recordTrajectory`, `redactForEmission`, `referenceFaultProfile`, `replayIncident`, `ReplayIncidentOptions`, `replayTrajectory`, `ReplayTrajectoryOptions`, `runAdversarialContract`, `runFailureScenario`, `runFaultProfile`, `RunFaultProfileOptions`, `runtimeAdapterCertificationProfile`, `runTrajectory`, `shapeOf`, `storageAdapterCertificationProfile`, `testSession`, `TestSession`, `TestSessionOptions`, `testToolAdapter`, `TrajectoryInvariantId`, `TrajectoryInvariantOptions`, `TrajectoryInvariantResult`, `trajectoryRegressionId`, `TrajectoryReplayResult`, `TrajectoryRun`, `TrajectoryTranscript`, `verifyAdapterCertification`
11684
+ `AdapterCertificationError`, `AdapterCertificationProfile`, `AdapterCertificationReport`, `AdversarialContractError`, `AdversarialContractOptions`, `AdversarialContractReport`, `AdversarialContractResult`, `AgentEvalCase`, `AgentEvalComponentEvidence`, `AgentEvalCompositionOptions`, `AgentEvalCompositionReport`, `AgentEvalRegressionError`, `AgentEvalReport`, `AgentEvalSuite`, `AgentEvalSuiteSpec`, `AgentFailureMatrixCase`, `AgentFailureMatrixError`, `AgentFailureMatrixKind`, `AgentFailureMatrixReport`, `AppLike`, `assertAdapterCertification`, `assertAdversarialContract`, `assertAgentEval`, `assertAgentEvalBaseline`, `assertAgentFailureMatrix`, `assertIncidentReplays`, `assertTrajectoryInvariants`, `BaselineComparison`, `BaselineOptions`, `cacheStoreCertificationProfile`, `CapturedRequest`, `CapturedRequestInput`, `captureIncident`, `CaptureIncidentOptions`, `CaseComparison`, `CaseResult`, `CertifiableCacheEntry`, `CertifiableCacheStore`, `CertifiableDeploymentAdapter`, `CertifiableDescriptorAdapter`, `CertifiableDomainEvent`, `CertifiableEventDeliveryAdapter`, `CertifiableEventRecord`, `CertifiableJobStore`, `CertifiableModelGateway`, `CertifiableRuntimeAdapter`, `CertifiableRuntimeServer`, `CertifiableStorageAdapter`, `CertificationCapabilityEvidence`, `CertificationCheck`, `CertificationCheckEvidence`, `certifyAdapter`, `checkTrajectoryInvariants`, `compareAgentEvalBaseline`, `ComparisonCode`, `ContractCaseContext`, `ContractCaseKind`, `ContractCoverageGap`, `ContractCoverageGapCode`, `ContractLabHandler`, `ContractLabRuntimeAdapter`, `ContractLabServer`, `ContractLabWitness`, `contractLabWitnesses`, `ContractReplay`, `ContractRuntime`, `ContractTarget`, `ContractTestApp`, `ContractWitness`, `cookieJar`, `CookieJar`, `createFailureLab`, `createReferenceContractLabHandler`, `createTrajectoryTranscript`, `CreateTrajectoryTranscriptOptions`, `defineAgentEvalSuite`, `defineCertificationProfile`, `defineFaultProfile`, `deploymentAdapterCertificationProfile`, `deploymentCertificationProfile`, `EffectLedger`, `eventDeliveryCertificationProfile`, `FailureDirective`, `FailureEvidence`, `FailureInjectedError`, `FailureKind`, `FailureLab`, `FailureLabOptions`, `FailureReplay`, `FailureScenario`, `FailureScenarioReport`, `FaultProfile`, `FaultProfileFault`, `FaultProfileFaultKind`, `FaultProfileReport`, `FaultProfileScenario`, `FaultProfileScenarioReport`, `gatewayCertificationProfile`, `generateRegressionTest`, `GenerateRegressionTestOptions`, `httpToolAdapter`, `IdempotencyDivergence`, `IdempotencyProof`, `IncidentCapsule`, `IncidentReplayError`, `IncidentReplayResult`, `inProcessToolAdapter`, `jobStoreCertificationProfile`, `mcpToolAdapter`, `modelGatewayCertificationProfile`, `parseRubricVerdict`, `proveIdempotency`, `recordTrajectory`, `redactForEmission`, `referenceFaultProfile`, `registryCertificationProfile`, `replayIncident`, `ReplayIncidentOptions`, `replayTrajectory`, `ReplayTrajectoryOptions`, `RubricShape`, `RubricSpec`, `RubricVerdict`, `runAdversarialContract`, `runAgentEvalComposition`, `runAgentFailureMatrix`, `runContractLab`, `runContractLabOverHttp`, `runContractLabThroughAdapter`, `runFailureScenario`, `runFaultProfile`, `RunFaultProfileOptions`, `runtimeAdapterCertificationProfile`, `runTrajectory`, `ScoreTolerance`, `shapeOf`, `storageAdapterCertificationProfile`, `testSession`, `TestSession`, `TestSessionOptions`, `testToolAdapter`, `TrajectoryInvariantId`, `TrajectoryInvariantOptions`, `TrajectoryInvariantResult`, `trajectoryRegressionId`, `TrajectoryReplayResult`, `TrajectoryRun`, `TrajectoryTranscript`, `verifyAdapterCertification`
10367
11685
 
10368
11686
  ## @nifrajs/ts-plugin
10369
11687
 
@@ -10375,7 +11693,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10375
11693
 
10376
11694
  ## @nifrajs/web
10377
11695
 
10378
- `Action`, `ACTION_GLOBAL`, `apiKey`, `assertRenderAdapterConformance`, `assertStaticBoundaryImports`, `Blocker`, `BlockerController`, `BlockerFunction`, `BlockerLocation`, `BlockerState`, `Boundary`, `BOUNDARY_GLOBAL`, `BoundaryDescriptor`, `boundaryDescriptors`, `BoundaryError`, `BoundaryMode`, `boundaryModeKey`, `BoundaryRegistration`, `BoundaryRequestCtx`, `BoundaryState`, `BoundaryStates`, `BoundaryStatus`, `BrowserNavigate`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `ClientAction`, `ClientActionArgs`, `ClientActionResult`, `clientEntry`, `ClientLoader`, `ClientLoaderArgs`, `ClientRequestBody`, `ClientRouteHooks`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createMutation`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `DehydratedState`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `DynamicBoundary`, `DynamicBoundaryBatch`, `enableDraft`, `EnableDraftOptions`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `gate`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateRouteSearchTypes`, `GenerateRouteSearchTypesOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `getBrowserNavigate`, `GetStaticPaths`, `gone`, `hashQueryKey`, `IDLE_BLOCKER`, `InertScriptType`, `InfiniteData`, `InfiniteQueryHandle`, `InfiniteQueryOptions`, `InterceptBoundary`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_REVALIDATE_TAGS_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LAYOUT_DATA_GLOBAL`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `manifest`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `MemoryStaticBoundaryCache`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaDescriptor`, `MetaInput`, `MountRouterOptions`, `MutationCallbacks`, `MutationHandle`, `MutationState`, `MutationStatus`, `NAV_FROM_HEADER`, `NavigateFunction`, `NavigateOptions`, `NavigateSearchOf`, `NavigateTargetInput`, `notFound`, `openGraph`, `OpenGraphInput`, `PendingBoundary`, `PRE_HYDRATION_GUARD`, `previewEndpoint`, `PreviewEndpointOptions`, `PublicDirCache`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryOptions`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `registerBlocker`, `RenderAdapter`, `RenderAdapterConformanceError`, `RenderAdapterConformanceFixture`, `RenderAssemblyCache`, `RenderedPage`, `renderPage`, `RenderPageInput`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveDynamicBoundaries`, `resolveMeta`, `resolveNavigate`, `resolvePublicPath`, `resolveStaticBoundaries`, `RETAIN_HEADER`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROOT_ATTRIBUTE`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `RouteSearch`, `routeStyles`, `ScriptDescriptor`, `searchOf`, `SearchOf`, `searchOfChain`, `serializeData`, `serializeSearch`, `servePublicDir`, `ServePublicDirOptions`, `SERVER_FN_MODULE`, `SERVER_ONLY_MODULE`, `ServerOnly`, `setBlockerController`, `setBrowserNavigate`, `setSsrModuleLoader`, `ssrModuleLoader`, `SsrModuleLoader`, `startDynamicBoundaries`, `StaticBoundary`, `StaticBoundaryCache`, `StaticBoundaryImportEdge`, `StaticBoundaryRoot`, `StaticCtx`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `STATUS_HEADER`, `statusPage`, `StatusPageOptions`, `styles`, `Submission`, `SubmitOptions`, `unsafeInlineScript`, `UnsafeScriptDescriptor`, `withISR`
11696
+ `Action`, `ACTION_GLOBAL`, `assertRenderAdapterConformance`, `assertStaticBoundaryImports`, `Blocker`, `BlockerController`, `BlockerFunction`, `BlockerLocation`, `BlockerState`, `Boundary`, `BOUNDARY_GLOBAL`, `BoundaryDescriptor`, `boundaryDescriptors`, `BoundaryError`, `BoundaryMode`, `boundaryModeKey`, `BoundaryRegistration`, `BoundaryRequestCtx`, `BoundaryState`, `BoundaryStates`, `BoundaryStatus`, `BrowserNavigate`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `ClientAction`, `ClientActionArgs`, `ClientActionResult`, `ClientLoader`, `ClientLoaderArgs`, `ClientRequestBody`, `ClientRouteHooks`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createMutation`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `DehydratedState`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `DynamicBoundary`, `DynamicBoundaryBatch`, `enableDraft`, `EnableDraftOptions`, `enumerateStaticRoutes`, `Fetcher`, `FetcherState`, `FetchRouteData`, `filePathToPattern`, `filePathToPatterns`, `FontDisplay`, `fontFace`, `FontFace`, `fontPreload`, `FontPreloadInput`, `FontSource`, `generateClientEntry`, `GenerateClientEntryOptions`, `generateRouteSearchTypes`, `GenerateRouteSearchTypesOptions`, `generateServerManifest`, `GenerateServerManifestOptions`, `getBrowserNavigate`, `GetStaticPaths`, `gone`, `hashQueryKey`, `IDLE_BLOCKER`, `InertScriptType`, `InfiniteData`, `InfiniteQueryHandle`, `InfiniteQueryOptions`, `InterceptBoundary`, `isDraftEnabled`, `ISR_REVALIDATE_HEADER`, `ISR_REVALIDATE_TAGS_HEADER`, `ISR_STATUS_HEADER`, `ISRApp`, `ISROptions`, `ISRPlatform`, `jsonLd`, `KVCacheStore`, `KVCacheStoreOptions`, `KVNamespaceLike`, `LAYOUT_DATA_GLOBAL`, `LayoutEntry`, `LinkDescriptor`, `Loader`, `LoaderContext`, `Manifest`, `MemoryCacheStore`, `MemoryCacheStoreOptions`, `MemoryStaticBoundaryCache`, `mergeHeads`, `Meta`, `MetaArgs`, `MetaDescriptor`, `MetaInput`, `MountRouterOptions`, `MutationCallbacks`, `MutationHandle`, `MutationState`, `MutationStatus`, `NAV_FROM_HEADER`, `NavigateFunction`, `NavigateOptions`, `NavigateSearchOf`, `NavigateTargetInput`, `notFound`, `openGraph`, `OpenGraphInput`, `PendingBoundary`, `PRE_HYDRATION_GUARD`, `previewEndpoint`, `PreviewEndpointOptions`, `PublicDirCache`, `QueryClient`, `QueryClientOptions`, `QueryHandle`, `QueryOptions`, `QueryState`, `QueryStatus`, `redirect`, `REDIRECT_HEADER`, `RedirectOptions`, `registerBlocker`, `RenderAdapter`, `RenderAdapterConformanceError`, `RenderAdapterConformanceFixture`, `RenderAssemblyCache`, `RenderedPage`, `renderPage`, `RenderPageInput`, `RenderPageOptions`, `renderPageResult`, `RenderProps`, `resolveDynamicBoundaries`, `resolveMeta`, `resolveNavigate`, `resolvePublicPath`, `resolveStaticBoundaries`, `RETAIN_HEADER`, `revalidate`, `REVALIDATE_HEADER`, `revalidateEndpoint`, `RevalidateEndpointOptions`, `RevalidateResult`, `ROOT_ATTRIBUTE`, `ROUTE_GLOBAL`, `RouteEntry`, `RouteMatch`, `RouteModule`, `RoutePattern`, `RouterState`, `RouteSearch`, `ScriptDescriptor`, `searchOf`, `SearchOf`, `searchOfChain`, `serializeData`, `serializeSearch`, `servePublicDir`, `ServePublicDirOptions`, `SERVER_FN_MODULE`, `SERVER_ONLY_MODULE`, `ServerOnly`, `setBlockerController`, `setBrowserNavigate`, `setSsrModuleLoader`, `ssrModuleLoader`, `SsrModuleLoader`, `startDynamicBoundaries`, `StaticBoundary`, `StaticBoundaryCache`, `StaticBoundaryImportEdge`, `StaticBoundaryRoot`, `StaticCtx`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `STATUS_HEADER`, `statusPage`, `StatusPageOptions`, `Submission`, `SubmitOptions`, `unsafeInlineScript`, `UnsafeScriptDescriptor`, `withISR`
10379
11697
 
10380
11698
  ## @nifrajs/web-preact
10381
11699