@nifrajs/cli 2.14.1 → 3.2.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 (203) 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 -2346
  19. package/dist/check.js.map +1 -1
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +285 -10
  22. package/dist/cli.js.map +1 -1
  23. package/dist/command-catalog.d.ts +27 -1
  24. package/dist/command-catalog.d.ts.map +1 -1
  25. package/dist/command-catalog.js +142 -2
  26. package/dist/command-catalog.js.map +1 -1
  27. package/dist/doctor.d.ts +42 -0
  28. package/dist/doctor.d.ts.map +1 -1
  29. package/dist/doctor.js +64 -7
  30. package/dist/doctor.js.map +1 -1
  31. package/dist/env-file.d.ts +17 -0
  32. package/dist/env-file.d.ts.map +1 -0
  33. package/dist/env-file.js +113 -0
  34. package/dist/env-file.js.map +1 -0
  35. package/dist/fix-recipes.d.ts.map +1 -1
  36. package/dist/fix-recipes.js +91 -15
  37. package/dist/fix-recipes.js.map +1 -1
  38. package/dist/frontend-guidance.d.ts +57 -0
  39. package/dist/frontend-guidance.d.ts.map +1 -0
  40. package/dist/frontend-guidance.js +195 -0
  41. package/dist/frontend-guidance.js.map +1 -0
  42. package/dist/internal/reserved-segment-codemod.d.ts +36 -0
  43. package/dist/internal/reserved-segment-codemod.d.ts.map +1 -0
  44. package/dist/internal/reserved-segment-codemod.js +134 -0
  45. package/dist/internal/reserved-segment-codemod.js.map +1 -0
  46. package/dist/internal/typescript-import.d.ts +1 -1
  47. package/dist/internal/typescript-import.d.ts.map +1 -1
  48. package/dist/internal/typescript-import.js +62 -11
  49. package/dist/internal/typescript-import.js.map +1 -1
  50. package/dist/introspect.d.ts +26 -1
  51. package/dist/introspect.d.ts.map +1 -1
  52. package/dist/introspect.js +74 -24
  53. package/dist/introspect.js.map +1 -1
  54. package/dist/levels-tool.d.ts.map +1 -1
  55. package/dist/levels-tool.js +6 -0
  56. package/dist/levels-tool.js.map +1 -1
  57. package/dist/load.d.ts.map +1 -1
  58. package/dist/load.js +4 -1
  59. package/dist/load.js.map +1 -1
  60. package/dist/mcp-context.d.ts +48 -0
  61. package/dist/mcp-context.d.ts.map +1 -0
  62. package/dist/mcp-context.js +215 -0
  63. package/dist/mcp-context.js.map +1 -0
  64. package/dist/mcp-docs-tools.d.ts.map +1 -1
  65. package/dist/mcp-docs-tools.js +31 -0
  66. package/dist/mcp-docs-tools.js.map +1 -1
  67. package/dist/mcp-exec.d.ts +74 -0
  68. package/dist/mcp-exec.d.ts.map +1 -0
  69. package/dist/mcp-exec.js +844 -0
  70. package/dist/mcp-exec.js.map +1 -0
  71. package/dist/mcp-http.js +1 -1
  72. package/dist/mcp-http.js.map +1 -1
  73. package/dist/mcp-io.d.ts +26 -0
  74. package/dist/mcp-io.d.ts.map +1 -0
  75. package/dist/mcp-io.js +100 -0
  76. package/dist/mcp-io.js.map +1 -0
  77. package/dist/mcp-reflect.d.ts +16 -0
  78. package/dist/mcp-reflect.d.ts.map +1 -0
  79. package/dist/mcp-reflect.js +101 -0
  80. package/dist/mcp-reflect.js.map +1 -0
  81. package/dist/mcp-root.d.ts +7 -0
  82. package/dist/mcp-root.d.ts.map +1 -1
  83. package/dist/mcp-root.js +51 -3
  84. package/dist/mcp-root.js.map +1 -1
  85. package/dist/mcp.d.ts +10 -84
  86. package/dist/mcp.d.ts.map +1 -1
  87. package/dist/mcp.js +13 -1170
  88. package/dist/mcp.js.map +1 -1
  89. package/dist/openapi-tool.d.ts +5 -10
  90. package/dist/openapi-tool.d.ts.map +1 -1
  91. package/dist/openapi-tool.js +21 -2
  92. package/dist/openapi-tool.js.map +1 -1
  93. package/dist/openapi-types.d.ts +21 -0
  94. package/dist/openapi-types.d.ts.map +1 -0
  95. package/dist/openapi-types.js +214 -0
  96. package/dist/openapi-types.js.map +1 -0
  97. package/dist/port.d.ts.map +1 -1
  98. package/dist/port.js +2 -1
  99. package/dist/port.js.map +1 -1
  100. package/dist/project-facts.d.ts +2 -1
  101. package/dist/project-facts.d.ts.map +1 -1
  102. package/dist/project-facts.js.map +1 -1
  103. package/dist/recipes/3.0.0.d.ts +10 -0
  104. package/dist/recipes/3.0.0.d.ts.map +1 -0
  105. package/dist/recipes/3.0.0.js +23 -0
  106. package/dist/recipes/3.0.0.js.map +1 -0
  107. package/dist/recipes/index.d.ts.map +1 -1
  108. package/dist/recipes/index.js +2 -0
  109. package/dist/recipes/index.js.map +1 -1
  110. package/dist/release-verification.d.ts +4 -1
  111. package/dist/release-verification.d.ts.map +1 -1
  112. package/dist/release-verification.js +6 -123
  113. package/dist/release-verification.js.map +1 -1
  114. package/dist/rules/codes.d.ts +4 -0
  115. package/dist/rules/codes.d.ts.map +1 -1
  116. package/dist/rules/codes.js +4 -0
  117. package/dist/rules/codes.js.map +1 -1
  118. package/dist/rules/islands.d.ts +4 -0
  119. package/dist/rules/islands.d.ts.map +1 -0
  120. package/dist/rules/islands.js +155 -0
  121. package/dist/rules/islands.js.map +1 -0
  122. package/dist/rules/nano.d.ts +6 -0
  123. package/dist/rules/nano.d.ts.map +1 -0
  124. package/dist/rules/nano.js +264 -0
  125. package/dist/rules/nano.js.map +1 -0
  126. package/dist/rules/routes.d.ts.map +1 -1
  127. package/dist/rules/routes.js +17 -30
  128. package/dist/rules/routes.js.map +1 -1
  129. package/dist/rules/security.d.ts.map +1 -1
  130. package/dist/rules/security.js +38 -4
  131. package/dist/rules/security.js.map +1 -1
  132. package/dist/scaffold.d.ts +12 -6
  133. package/dist/scaffold.d.ts.map +1 -1
  134. package/dist/scaffold.js +149 -10
  135. package/dist/scaffold.js.map +1 -1
  136. package/dist/search-terms.d.ts.map +1 -1
  137. package/dist/search-terms.js +5 -3
  138. package/dist/search-terms.js.map +1 -1
  139. package/dist/stylex-migrate.d.ts +38 -0
  140. package/dist/stylex-migrate.d.ts.map +1 -0
  141. package/dist/stylex-migrate.js +1052 -0
  142. package/dist/stylex-migrate.js.map +1 -0
  143. package/dist/sync-manifest.d.ts.map +1 -1
  144. package/dist/sync-manifest.js +2 -1
  145. package/dist/sync-manifest.js.map +1 -1
  146. package/dist/verification-plan.d.ts +24 -0
  147. package/dist/verification-plan.d.ts.map +1 -0
  148. package/dist/verification-plan.js +116 -0
  149. package/dist/verification-plan.js.map +1 -0
  150. package/dist/work-graph.d.ts.map +1 -1
  151. package/dist/work-graph.js +6 -3
  152. package/dist/work-graph.js.map +1 -1
  153. package/dist/workspace-link.d.ts +37 -0
  154. package/dist/workspace-link.d.ts.map +1 -0
  155. package/dist/workspace-link.js +72 -0
  156. package/dist/workspace-link.js.map +1 -0
  157. package/docs/examples.json +37 -1
  158. package/docs/llms-full.txt +1598 -44
  159. package/docs/types.json +5157 -172
  160. package/package.json +9 -9
  161. package/src/assure-hydration.ts +6 -1
  162. package/src/assure.ts +4 -2
  163. package/src/check-diagnostics.ts +1219 -0
  164. package/src/check-scan.ts +1825 -0
  165. package/src/check.ts +47 -2731
  166. package/src/cli.ts +291 -10
  167. package/src/command-catalog.ts +177 -2
  168. package/src/doctor.ts +99 -8
  169. package/src/env-file.ts +121 -0
  170. package/src/fix-recipes.ts +101 -15
  171. package/src/frontend-guidance.ts +259 -0
  172. package/src/internal/reserved-segment-codemod.ts +150 -0
  173. package/src/internal/typescript-import.ts +61 -11
  174. package/src/introspect.ts +82 -21
  175. package/src/levels-tool.ts +6 -0
  176. package/src/load.ts +4 -1
  177. package/src/mcp-context.ts +266 -0
  178. package/src/mcp-docs-tools.ts +37 -0
  179. package/src/mcp-exec.ts +1040 -0
  180. package/src/mcp-http.ts +1 -1
  181. package/src/mcp-io.ts +118 -0
  182. package/src/mcp-reflect.ts +140 -0
  183. package/src/mcp-root.ts +62 -4
  184. package/src/mcp.ts +61 -1436
  185. package/src/openapi-tool.ts +32 -2
  186. package/src/openapi-types.ts +261 -0
  187. package/src/port.ts +2 -1
  188. package/src/project-facts.ts +2 -4
  189. package/src/recipes/3.0.0.ts +24 -0
  190. package/src/recipes/index.ts +2 -0
  191. package/src/release-verification.ts +22 -147
  192. package/src/rules/codes.ts +4 -0
  193. package/src/rules/islands.ts +171 -0
  194. package/src/rules/nano.ts +295 -0
  195. package/src/rules/routes.ts +17 -30
  196. package/src/rules/security.ts +40 -4
  197. package/src/scaffold.ts +169 -9
  198. package/src/search-terms.ts +7 -3
  199. package/src/stylex-migrate.ts +1139 -0
  200. package/src/sync-manifest.ts +2 -1
  201. package/src/verification-plan.ts +242 -0
  202. package/src/work-graph.ts +6 -3
  203. package/src/workspace-link.ts +80 -0
@@ -320,7 +320,7 @@ renders in its place, wrapped by the layouts at/above that segment, at status 50
320
320
  non-hydrated). On the **client** - a render error during navigation/interaction is caught by
321
321
  the nearest boundary, which renders `_error` in place (all five adapters). It
322
322
  receives the serialized error as `{ data: { name, message } }` (never the
323
- stack); a thrown `Response` (e.g. a guard `redirect`) passes through.
323
+ stack); a thrown control-flow value (e.g. a guard `redirect`) passes through.
324
324
 
325
325
  ```ts
326
326
  routes/
@@ -1225,6 +1225,374 @@ if (document.documentElement.hasAttribute("data-nifra-hydrated")) start()
1225
1225
  else document.addEventListener("nifra:hydrated", start, { once: true })
1226
1226
  ```
1227
1227
 
1228
+ ## Nifra - Islands: the AI-safe interactivity lane
1229
+
1230
+ > 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.
1231
+
1232
+ A `@nifrajs/web-vanilla` page ships zero framework runtime. When one corner needs
1233
+ to be interactive, you don't turn on hydration - you mount an **island**: a
1234
+ small, plain-DOM enhancer over a server-rendered element. Explicit imperative code with no
1235
+ hidden reactivity - the shape an AI agent (or a human) gets right on the first try.
1236
+
1237
+ ### Why islands, not hydration
1238
+
1239
+ Vanilla routes set `export const hydrate = false` - there is no client framework
1240
+ to hydrate with, and that is the point. The five framework adapters give you a reactive
1241
+ runtime and pay for it in client JS and in the subtle failure modes (stale closures,
1242
+ hydration mismatch, what-re-runs-when) that trip up generated code. Islands take the other
1243
+ trade: no runtime, no reactivity to get wrong. You wire the DOM by hand and hand back a
1244
+ teardown. Reach for a framework adapter when you genuinely need a stateful app UI; reach for
1245
+ islands for widgets on an otherwise-static page.
1246
+
1247
+ ### Mount an island
1248
+
1249
+ Render a `<nifra-island>` marker in your page (props are inline JSON), then
1250
+ bundle one entry that calls `mountIslands`. Load it via the route&apos;s
1251
+ `islandScripts`.
1252
+
1253
+ ```ts
1254
+ // doc-check: skip - server-rendered island marker (HTML, not a checkable module).
1255
+ // In your @nifrajs/web-vanilla page, render the host element with inline JSON props:
1256
+ html`<nifra-island data-id="counter" data-props=${JSON.stringify({ start: 0 })}>
1257
+ <output>0</output>
1258
+ <button type="button">+1</button>
1259
+ </nifra-island>`
1260
+ ```
1261
+
1262
+ ```ts
1263
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1264
+ import { defineIsland, mountIslands } from "@nifrajs/web/islands"
1265
+
1266
+ // defineIsland pins the data-props type; the body is plain imperative DOM. ALWAYS return the
1267
+ // teardown for anything you wire - mountIslands calls it on soft-nav (NF-C020 warns if you forget).
1268
+ const counter = defineIsland<{ start: number }>((el, props) => {
1269
+ let n = props.start
1270
+ const out = el.querySelector("output")!
1271
+ const onClick = () => { out.textContent = String(++n) }
1272
+ el.querySelector("button")?.addEventListener("click", onClick)
1273
+ return () => el.querySelector("button")?.removeEventListener("click", onClick)
1274
+ })
1275
+
1276
+ // One bundle entry, loaded via the route's `islandScripts`.
1277
+ mountIslands({ counter })
1278
+ ```
1279
+
1280
+ `defineIsland<Props>` is a zero-cost identity helper - its only job is to
1281
+ type `props` and `el` so the body needs no cast. Keep the body
1282
+ imperative: read the DOM, add listeners, **return the cleanup**. The returned
1283
+ function runs on soft-nav teardown; forget it and every navigation leaks the listener -
1284
+ `nifra check` flags exactly that as `NF-C020`.
1285
+
1286
+ ### Coordinate islands with a typed bus
1287
+
1288
+ Islands are isolated by design - each enhances one element and shares no state. When two must
1289
+ talk (an &ldquo;add to cart&rdquo; button and a cart badge), use a
1290
+ `createIslandBus`: one typed channel, created once and closed over by each
1291
+ enhancer. No implicit global, so concurrent renders and tests never cross-talk.
1292
+
1293
+ ```ts
1294
+ import { createIslandBus } from "@nifrajs/web/islands"
1295
+
1296
+ // One typed channel, created once and closed over by every enhancer that needs it.
1297
+ type CartEvents = { "cart:add": { sku: string }; "cart:count": number }
1298
+ const bus = createIslandBus<CartEvents>()
1299
+
1300
+ const off = bus.on("cart:count", (n) => console.log("cart now has", n, "items"))
1301
+ bus.emit("cart:add", { sku: "ABC-1" })
1302
+ off() // unsubscribe - also usable directly as an island's cleanup
1303
+ ```
1304
+
1305
+ The bus is synchronous, in-memory, and carries no history - a subscriber sees only events
1306
+ emitted after it subscribes. A throwing handler is isolated from the others. Because
1307
+ `on()` returns its own unsubscribe, an enhancer whose only job is to listen can
1308
+ return it directly as its cleanup:
1309
+
1310
+ ```ts
1311
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1312
+ import { createIslandBus, defineIsland, mountIslands } from "@nifrajs/web/islands"
1313
+
1314
+ type CartEvents = { "cart:add": { sku: string }; "cart:count": number }
1315
+ const bus = createIslandBus<CartEvents>()
1316
+
1317
+ // Two islands that never import each other - they meet only on the bus.
1318
+ const addButton = defineIsland<{ sku: string }>((el, props) => {
1319
+ const onClick = () => bus.emit("cart:add", { sku: props.sku })
1320
+ el.addEventListener("click", onClick)
1321
+ return () => el.removeEventListener("click", onClick)
1322
+ })
1323
+
1324
+ let count = 0
1325
+ const badge = defineIsland((el) => {
1326
+ const unsubscribe = bus.on("cart:add", () => {
1327
+ count += 1
1328
+ el.textContent = String(count)
1329
+ bus.emit("cart:count", count)
1330
+ })
1331
+ return unsubscribe // the on() unsubscribe IS the cleanup
1332
+ })
1333
+
1334
+ mountIslands({ addButton, badge })
1335
+ ```
1336
+
1337
+ ### Pattern: filter drives results
1338
+
1339
+ The same shape scales to any &ldquo;one island changes, another reacts&rdquo; case. Each
1340
+ island owns only its own DOM; they meet on the channel, never in a shared store.
1341
+
1342
+ ```ts
1343
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1344
+ import { createIslandBus, defineIsland, mountIslands } from "@nifrajs/web/islands"
1345
+
1346
+ // filter -> results, one directional channel. The results island owns its own DOM; the filter
1347
+ // island owns none of it. No shared store, no reactive tree - a message and two enhancers.
1348
+ const bus = createIslandBus<{ "filter:set": string }>()
1349
+
1350
+ const filter = defineIsland((el) => {
1351
+ const input = el.querySelector("input")!
1352
+ const onInput = () => bus.emit("filter:set", input.value.toLowerCase())
1353
+ input.addEventListener("input", onInput)
1354
+ return () => input.removeEventListener("input", onInput)
1355
+ })
1356
+
1357
+ const results = defineIsland((el) =>
1358
+ bus.on("filter:set", (q) => {
1359
+ for (const row of el.querySelectorAll<HTMLElement>("[data-name]")) {
1360
+ const name = row.dataset.name ?? ""
1361
+ row.hidden = q !== "" && !name.toLowerCase().includes(q)
1362
+ }
1363
+ }),
1364
+ )
1365
+
1366
+ mountIslands({ filter, results })
1367
+ ```
1368
+
1369
+ ### Load strategies
1370
+
1371
+ A marker&apos;s `data-strategy` defers its enhancer: `load` (default),
1372
+ `idle`, `visible` (mount when it scrolls into view), or a
1373
+ `media` query. An island with no matching enhancer, or an invalid strategy, stays
1374
+ inert server-rendered HTML - forward-compatible, never a runtime error.
1375
+
1376
+ ## Nifra - nano: explicit reactivity for small apps
1377
+
1378
+ > 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.
1379
+
1380
+ `@nifrajs/web/nano` is the middle lane between a static
1381
+ [island](/docs/islands) and a full framework adapter. It gives you a signal, a
1382
+ derived `computed`, and a keyed list - enough to build a small app with local
1383
+ state a human edits - and nothing else. No virtual DOM, no template compiler, no
1384
+ auto-tracking. Every reactive edge is a call you write, which is exactly what lets a tool
1385
+ catch the mistakes before runtime.
1386
+
1387
+ ### Why a fourth lane
1388
+
1389
+ A framework fails generated code not because it is unfamiliar but because its reactivity is
1390
+ _implicit_: an effect silently tracks what it read, a stale closure captures the wrong
1391
+ value, a render mismatches hydration - all silent, all runtime. Preact fixes the
1392
+ **size** of that model (3 KB) but copies its footguns faithfully. nano
1393
+ fixes the **failure model** for the small-app band: dependencies are an explicit
1394
+ array, list keys are a required argument, cleanups are a returned value. Each of those is a
1395
+ thing a static check can see. Reach for nano when a page needs local state and a keyed list;
1396
+ reach for a [framework adapter](/docs/frameworks) the moment you need client
1397
+ routing, nested view state, or suspense.
1398
+
1399
+ ### Signals and computed
1400
+
1401
+ A `signal` holds a value; `.get()` reads it and `.set(...)`
1402
+ replaces it (deduped with `Object.is`). A `computed` derives a value and
1403
+ declares its sources as an explicit deps array - it recomputes when a listed source changes,
1404
+ never by magic. Both are synchronous and framework-free.
1405
+
1406
+ ```ts
1407
+ import { signal, computed } from "@nifrajs/web/nano"
1408
+
1409
+ // State is a signal: reads are .get(), writes are .set(...). Every edge is a call you can see.
1410
+ const count = signal(0)
1411
+
1412
+ // Derived state declares its dependencies EXPLICITLY - the array is the whole contract. It
1413
+ // recomputes when (and only when) a listed source changes. Object.is-deduped, like signal.
1414
+ const doubled = computed(() => count.get() * 2, [count])
1415
+
1416
+ const off = doubled.subscribe((v) => console.log("doubled is now", v))
1417
+ count.set(5) // logs: doubled is now 10
1418
+ off() // unsubscribe
1419
+ ```
1420
+
1421
+ The deps array is the whole contract, and its one failure mode is catchable: a
1422
+ `computed` whose body reads a signal the array omits will never recompute when that
1423
+ signal changes. `nifra check` flags exactly that as `NF-C023` - the
1424
+ check reads which signals the body calls `.get()` on and compares them to the
1425
+ array. A framework&apos;s auto-tracked effect gives a tool nothing to compare, which is why
1426
+ the equivalent lint elsewhere is a best-effort warning, not a gate.
1427
+
1428
+ ### Bind a signal to the DOM
1429
+
1430
+ `bind` is the one-value edge: it applies your function immediately and on every
1431
+ change, and returns a disposer. **Collect the disposer.** A bare
1432
+ `bind(...)` whose return value is discarded leaks its subscription on soft-nav -
1433
+ `nifra check` flags it as `NF-C021`.
1434
+
1435
+ ```ts
1436
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1437
+ import { signal, bind } from "@nifrajs/web/nano"
1438
+
1439
+ const label = signal("hello")
1440
+
1441
+ // bind applies immediately AND on every change; it returns the disposer. COLLECT it - a bare
1442
+ // bind(...) that drops its return leaks the subscription on soft-nav (nifra check flags NF-C021).
1443
+ const off = bind(document.querySelector("h1")!, label, (el, v) => { el.textContent = v })
1444
+ label.set("world") // the <h1> updates synchronously
1445
+ // ...on teardown: off()
1446
+ ```
1447
+
1448
+ ### Keyed lists with bindList
1449
+
1450
+ `bindList` keeps a container&apos;s children in sync with an array by key - a keyed
1451
+ reconcile like a framework&apos;s list diff, but you spell the key out. Add, remove, and
1452
+ reorder reuse the matching node (preserving focus and scroll) instead of rebuilding the list.
1453
+ Key by a **stable id** on the item; keying by the array index reuses the wrong
1454
+ node on reorder, and `nifra check` flags it as `NF-C022`.
1455
+
1456
+ ```ts
1457
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1458
+ import { signal, bindList } from "@nifrajs/web/nano"
1459
+
1460
+ interface Todo { id: string; text: string; done: boolean }
1461
+ const todos = signal<Todo[]>([])
1462
+
1463
+ // bindList keeps a container's children in sync with an array by KEY - a keyed reconcile, like a
1464
+ // framework's list diff, but explicit. Add/remove/reorder reuse the right node instead of rebuilding.
1465
+ const off = bindList(todos, document.querySelector("ul")!, {
1466
+ key: (t) => t.id, // a STABLE id on the item. Keying by the array index is NF-C022 - reorder breaks.
1467
+ create: (t) => { const li = document.createElement("li"); li.dataset.id = t.id; return li },
1468
+ update: (li, t) => { li.textContent = t.text; li.classList.toggle("done", t.done) },
1469
+ })
1470
+
1471
+ todos.set([...todos.get(), { id: crypto.randomUUID(), text: "buy milk", done: false }])
1472
+ // ...on teardown: off()
1473
+ ```
1474
+
1475
+ ### Async state with resource
1476
+
1477
+ `resource(fetcher, [deps])` is nano&apos;s answer to &ldquo;suspense&rdquo; - an
1478
+ async cell whose value is an explicit `&#123; status, value, error &#125;` union.
1479
+ It runs the fetcher immediately and again whenever a declared dep changes, and it handles the
1480
+ two things people get wrong by hand: the fetcher receives an `AbortSignal`, and a
1481
+ superseded request is aborted with its late result dropped, so the newest fetch always wins -
1482
+ no stale-response flicker. There is no thrown promise and no magic boundary; it is a value you
1483
+ read or bind like any other.
1484
+
1485
+ ```ts
1486
+ import { signal, resource } from "@nifrajs/web/nano"
1487
+
1488
+ const userId = signal(1)
1489
+
1490
+ // resource(fetcher, [deps]) is an async cell: it fetches immediately and again whenever a declared
1491
+ // dep changes. The fetcher gets an AbortSignal; a superseded fetch is aborted and its late result
1492
+ // dropped, so the newest request always wins - no stale-response flicker to hand-guard.
1493
+ const user = resource(async (sig) => {
1494
+ const res = await fetch(`/api/users/${userId.get()}`, { signal: sig })
1495
+ return (await res.json()) as { name: string }
1496
+ }, [userId])
1497
+
1498
+ // The value is an explicit union - status is "pending" | "error" | "ready", never a secret undefined.
1499
+ const off = user.subscribe((s) => {
1500
+ if (s.status === "ready") console.log("got", s.value.name)
1501
+ })
1502
+ userId.set(2) // refetches; the deps array is what NF-C023 checks against the fetcher's .get() reads
1503
+ // ...on teardown: off()
1504
+ ```
1505
+
1506
+ The deps array is checked exactly like `computed`: a `resource` whose
1507
+ fetcher reads a signal the array omits won&apos;t refetch when it changes, and
1508
+ `nifra check` flags it as `NF-C023`. Bind it to the DOM with
1509
+ `bindResource`, which dispatches on `status` - one handler per state,
1510
+ `ready` required, so a still-loading value can never render as a stray
1511
+ `undefined`.
1512
+
1513
+ ```ts
1514
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1515
+ import { signal, resource, bindResource } from "@nifrajs/web/nano"
1516
+
1517
+ const userId = signal(1)
1518
+ const user = resource(async (sig) => {
1519
+ const res = await fetch(`/api/users/${userId.get()}`, { signal: sig })
1520
+ return (await res.json()) as { name: string }
1521
+ }, [userId])
1522
+
1523
+ // bindResource dispatches on status - one handler per state, ready required. Like bind it returns a
1524
+ // disposer (a bare bindResource(...) that drops it is NF-C021).
1525
+ const off = bindResource(document.querySelector("[data-user]")!, user, {
1526
+ pending: (el) => { el.textContent = "Loading…" },
1527
+ ready: (el, u) => { el.textContent = u.name },
1528
+ error: (el) => { el.textContent = "Could not load user" },
1529
+ })
1530
+ // ...on teardown: off()
1531
+ ```
1532
+
1533
+ ### nano inside an island
1534
+
1535
+ nano owns state and DOM edges; an [island](/docs/islands) owns mount and teardown.
1536
+ Put the signals in the enhancer, collect every disposer, and return one cleanup that calls
1537
+ them all. Scaffold this shape with
1538
+ `nifra_scaffold &#123; path, variant: "stateful" &#125;` on a vanilla
1539
+ project - it emits the golden pattern below.
1540
+
1541
+ ```ts
1542
+ // doc-check: skip - browser DOM enhancer, typechecked in @nifrajs/web under its DOM lib.
1543
+ import { defineIsland, mountIslands } from "@nifrajs/web/islands"
1544
+ import { signal, computed, bind, bindList } from "@nifrajs/web/nano"
1545
+
1546
+ interface Todo { id: string; text: string; done: boolean }
1547
+
1548
+ // nano lives INSIDE an island - the island owns mount/teardown, nano owns the state and the DOM edges.
1549
+ const todos = defineIsland<{ items: Todo[] }>((el, props) => {
1550
+ const items = signal<Todo[]>(props.items)
1551
+ const remaining = computed(() => items.get().filter((t) => !t.done).length, [items])
1552
+
1553
+ // Collect every disposer; the island's cleanup calls them all (this is what NF-C021 protects).
1554
+ const cleanups: Array<() => void> = []
1555
+ cleanups.push(bind(el.querySelector("[data-count]")!, remaining, (n, v) => {
1556
+ n.textContent = String(v) + " left"
1557
+ }))
1558
+ cleanups.push(bindList(items, el.querySelector("[data-list]")!, {
1559
+ key: (t) => t.id,
1560
+ create: (t) => { const li = document.createElement("li"); li.dataset.id = t.id; return li },
1561
+ update: (li, t) => { li.textContent = t.text; li.classList.toggle("done", t.done) },
1562
+ }))
1563
+
1564
+ const add = (text: string) =>
1565
+ items.set([...items.get(), { id: crypto.randomUUID(), text, done: false }])
1566
+ void add
1567
+
1568
+ return () => { for (const off of cleanups) off() }
1569
+ })
1570
+
1571
+ mountIslands({ todos })
1572
+ ```
1573
+
1574
+ ### The three checks
1575
+
1576
+ nano&apos;s explicitness is the whole point: because each reactive edge is a visible call, its
1577
+ three mistakes are static lints with a fix recipe - the same closed loop (`scaffold`
1578
+ &rarr; `check` &rarr; `fix` &rarr; `verify`) that makes the
1579
+ backend AI-safe, now on the frontend.
1580
+
1581
+ -
1582
+ `NF-C021` - a `bind`/`bindList` whose disposer is
1583
+ discarded. Collect it; call it on teardown.
1584
+
1585
+ -
1586
+ `NF-C022` - a `bindList` keyed by the array index. Key by a stable id.
1587
+
1588
+ -
1589
+ `NF-C023` - a `computed` that reads a signal its deps omit. Add it to
1590
+ the deps array.
1591
+
1592
+ All three _warn_ - a false positive must never fail a build - and each carries a fix
1593
+ recipe an agent can apply. Where nano stops (client routing, nested views, suspense), stop
1594
+ adding nano and reach for `@nifrajs/web-preact` or another framework adapter.
1595
+
1228
1596
  ## Nifra - Streaming
1229
1597
 
1230
1598
  > Streaming SSR, Suspense, and defer() in Nifra - on every runtime including the edge.
@@ -1607,6 +1975,8 @@ Node against the outcome's plain header record on the **direct socket writer**,
1607
1975
  ever materializing Web `Request`/`Response` objects:
1608
1976
 
1609
1977
  ```ts
1978
+ import { responseObserver } from "@nifrajs/core/response-observer"
1979
+ import { server } from "@nifrajs/core/server"
1610
1980
  import type { Middleware } from "@nifrajs/core/server"
1611
1981
 
1612
1982
  // ONE implementation, fast on every runtime: on Bun/Deno it mutates the
@@ -1619,6 +1989,8 @@ export function serverName(value: string): Middleware {
1619
1989
  },
1620
1990
  }
1621
1991
  }
1992
+
1993
+ const app = server().use(responseObserver()).use(serverName("nifra"))
1622
1994
  ```
1623
1995
 
1624
1996
  Why this matters on Node: a full `onResponse(res: Response)` hook needs real Web
@@ -2799,9 +3171,10 @@ createWebApp({ /* … */ api: sessions }) // inject the manager
2799
3171
 
2800
3172
  ### Guard a route
2801
3173
 
2802
- `requireSession` / `requireUser` throw a `Response` (a 302 to
2803
- `redirectTo`, or a 401) when the session is missing - Nifra returns a thrown Response
2804
- as-is, so the guard short-circuits the loader.
3174
+ `requireSession` / `requireUser` throw a `status(...)` render
3175
+ (a 302 to `redirectTo`, or a 401) when the session is missing - Nifra renders a
3176
+ thrown control-flow value as-is, so the guard short-circuits the loader. It is plain data, not
3177
+ a `Response`: same bytes on the wire, on the lane an ordinary return takes.
2805
3178
 
2806
3179
  ```ts
2807
3180
  // doc-check: skip - fragment: `api` is the session manager createWebApp injected (see setup above).
@@ -2962,6 +3335,7 @@ const image = createImageHandler({
2962
3335
  root: "./public", // local sources resolve under here (traversal+symlink guarded)
2963
3336
  allowedOrigins: ["https://cdn.example"], // remote sources: allowlist only (omit ⇒ none)
2964
3337
  // maxWidth, maxSourceBytes, maxSourcePixels, concurrency, cacheMaxAge - all tunable
3338
+ // immutable: true, // only for content-versioned source URLs
2965
3339
  })
2966
3340
 
2967
3341
  // mount it in your router:
@@ -2984,9 +3358,10 @@ probe), and a concurrency semaphore bounding the CPU-heavy codec work.
2984
3358
 
2985
3359
  -
2986
3360
  **Correct + cacheable.** Never upscales past the intrinsic width; negotiates WebP via
2987
- `Accept` (with `Vary: Accept`); serves
2988
- `Cache-Control: …, immutable` + a strong `ETag` computed _before_ any
2989
- decode, so a conditional `If-None-Match` short-circuits the whole pipeline.
3361
+ `Accept` (with `Vary: Accept`); and hashes the emitted bytes for a strong
3362
+ `ETag`, so a changed source at the same URL cannot receive a false `304`.
3363
+ The mutable-safe default is a one-hour `max-age`; opt into `immutable` only
3364
+ when every source URL is content-versioned.
2990
3365
 
2991
3366
  The handler reads the filesystem, so its _local-source_ path targets **Node/Bun servers**
2992
3367
  - but the codec itself is a pluggable `ImageBackend`, so it runs anywhere (see below).
@@ -3422,6 +3797,28 @@ app.ws("/chat", {
3422
3797
  })
3423
3798
  ```
3424
3799
 
3800
+ ### Optional outbound validation
3801
+
3802
+ `sendSchema` is type-level by default: it documents and types what the client
3803
+ receives with no runtime cost. Set `validateSend: true` to check server-sent JSON
3804
+ text and UTF-8 binary frames before the native socket sends them. Invalid frames and async
3805
+ validators fail closed and route a diagnostic to `error` when present. This is a
3806
+ contract check for your own output, not an inbound security boundary; the flag requires a
3807
+ `sendSchema` and is intentionally off by default.
3808
+
3809
+ ```ts
3810
+ // doc-check: skip - illustrative: outbound contract on an existing app.
3811
+ import { t } from "@nifrajs/schema"
3812
+
3813
+ app.ws("/events", {
3814
+ sendSchema: t.Object({ kind: t.Literal("ready"), id: t.String() }),
3815
+ validateSend: true, // opt in; default remains type-level only
3816
+ open(ws) {
3817
+ ws.send(JSON.stringify({ kind: "ready", id: "evt-1" }))
3818
+ },
3819
+ })
3820
+ ```
3821
+
3425
3822
  ### Pub/sub - app.publish
3426
3823
 
3427
3824
  `ws.subscribe(topic)` joins a topic; `app.publish(topic, data)`
@@ -3792,6 +4189,48 @@ export const vitePlugins = [react()] // dev HMR (Fast Refresh)
3792
4189
  // define = { __VUE_OPTIONS_API__: "true", ... } // Vue feature flags
3793
4190
  ```
3794
4191
 
4192
+ ### Tailwind to StyleX
4193
+
4194
+ Nifra includes a conservative codemod for moving static Tailwind classes to StyleX. It is a dry
4195
+ run by default, so inspect the proposed edits first:
4196
+
4197
+ Supported static `className` attributes become `stylex.props(...)` calls
4198
+ backed by a local `stylex.create(...)` table. Responsive breakpoints and
4199
+ element-local pseudo-classes such as `hover:` are supported. Dynamic class
4200
+ expressions, arbitrary values, parent-dependent variants, and unknown utilities stay unchanged
4201
+ and are reported with file/line evidence. A non-zero exit status means manual migration issues
4202
+ remain.
4203
+
4204
+ StyleX compilation is native to Nifra&apos;s Bun pipeline. Install the runtime and compiler peers,
4205
+ then register both the browser and SSR transforms:
4206
+
4207
+ ```ts
4208
+ # Install the runtime and the optional compiler peers.
4209
+ bun add @stylexjs/stylex
4210
+ bun add -d @babel/core @stylexjs/babel-plugin @babel/plugin-syntax-flow \
4211
+ @babel/plugin-syntax-jsx @babel/plugin-syntax-typescript
4212
+ ```
4213
+
4214
+ ```ts
4215
+ // nifra.config.ts - Bun's client and SSR build transforms.
4216
+ import { stylexBunPlugin } from "@nifrajs/web/plugins/stylex"
4217
+
4218
+ export const clientPlugins = [stylexBunPlugin("dom")]
4219
+ export const serverPlugins = [stylexBunPlugin("ssr")]
4220
+ ```
4221
+
4222
+ If the project intentionally uses Vite, add the Vite adapter alongside its existing Vite
4223
+ transforms. Nifra will use Vite when `vitePlugins` are the app&apos;s only transforms:
4224
+
4225
+ ```ts
4226
+ // doc-check: skip - requires the third-party Vite React plugin
4227
+ // If the app intentionally uses Vite, keep StyleX in vitePlugins.
4228
+ import react from "@vitejs/plugin-react"
4229
+ import { stylexVite } from "@nifrajs/web/plugins/stylex"
4230
+
4231
+ export const vitePlugins = [react(), stylexVite()]
4232
+ ```
4233
+
3795
4234
  ### Scope
3796
4235
 
3797
4236
  `nifra build` defaults to a self-hosted Bun server. Use `--target node`,
@@ -3801,7 +4240,7 @@ the command printed by the build.
3801
4240
 
3802
4241
  ## Nifra - Coding agents
3803
4242
 
3804
- > 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.
4243
+ > Nifra's agent layer: live MCP tools for coding agents, bounded typed turns, resumable evidence, a coding-agent host, browser views, A2A and AG-UI bridges, and token-only telemetry.
3805
4244
 
3806
4245
  Nifra ships an MCP server. It is not a documentation lookup bolted onto a framework: an agent
3807
4246
  can read the project, learn the real API, run actual requests against the backend it just
@@ -3966,8 +4405,8 @@ nifra levels --min 1
3966
4405
 
3967
4406
  ### Building agent features, not just serving them
3968
4407
 
3969
- The tools above let an agent work on your app. Three packages are for the opposite case, where
3970
- the app you are building is itself an AI feature.
4408
+ The tools above let an agent work on your app. Nifra also provides provider-neutral building
4409
+ blocks for the opposite case, where the app you are building is itself an AI feature.
3971
4410
 
3972
4411
  **
3973
4412
  `@nifrajs/prompt`
@@ -4009,6 +4448,99 @@ serves a SQLite database as its own MCP server, fail-closed: allowlisted schema
4009
4448
  default, and read-only queries only when you opt in, with plan verification. Handing a model a
4010
4449
  database connection is not the same as handing it a query tool, and this is the second one.
4011
4450
 
4451
+ ### Run a coding agent
4452
+
4453
+ `@nifrajs/agent-protocol` is the small versioned session and event contract.
4454
+ `@nifrajs/coding-agent` provides the standalone `nifra-agent` host with
4455
+ bounded sessions, checkpoints, workflows, extensions, verification, and authenticated local
4456
+ RPC. `@nifrajs/pi` is an optional Pi backend; other backends implement the same
4457
+ protocol port.
4458
+
4459
+ ```ts
4460
+ bun add @nifrajs/coding-agent @nifrajs/pi
4461
+
4462
+ # Interactive, one-shot, JSON, and local RPC modes are available.
4463
+ bunx nifra-agent --backend pi
4464
+ bunx nifra-agent --backend pi --message "run the checks and explain failures"
4465
+ ```
4466
+
4467
+ The public host keeps live queues and captured evidence bounded, filters verification process
4468
+ environments, and treats extensions as an explicit opt-in. The local process adapter and
4469
+ extension worker are crash-containment helpers, not hostile-code sandboxes.
4470
+
4471
+ ### Expose the same agent to other clients
4472
+
4473
+ `@nifrajs/a2a` mounts an A2A 1.0 card and JSON-RPC/SSE binding.
4474
+ `@nifrajs/ag-ui` mounts the AG-UI event stream. Both drive the same bounded runner,
4475
+ compose step evidence with telemetry, and support typed human-in-the-loop continuation. Model
4476
+ ports, state stores, capabilities, and approval transports are injected per request.
4477
+
4478
+ ```ts
4479
+ import { server } from "@nifrajs/core"
4480
+ import { mountA2A } from "@nifrajs/a2a"
4481
+ import { mountAgUI } from "@nifrajs/ag-ui"
4482
+ import type { AgentPorts } from "@nifrajs/agent"
4483
+ import { agent } from "./agent"
4484
+
4485
+ declare const myModelPort: AgentPorts["model"]
4486
+ declare const myStateStore: NonNullable<AgentPorts["state"]>
4487
+
4488
+ const app = server()
4489
+ const ports = () => ({
4490
+ model: myModelPort,
4491
+ capabilities: ["search.read"],
4492
+ state: myStateStore, // scope this to the request subject
4493
+ })
4494
+
4495
+ mountA2A(app, {
4496
+ agent,
4497
+ card: { url: "https://api.example.com/a2a", version: "1.0.0" },
4498
+ ports,
4499
+ })
4500
+ mountAgUI(app, { agent, ports })
4501
+
4502
+ // A2A: GET the card, POST JSON-RPC, optional SSE streaming.
4503
+ // AG-UI: POST RunAgentInput, receive the event stream over SSE.
4504
+ ```
4505
+
4506
+ These mounts deliberately do not authenticate callers: put the route behind your application's
4507
+ auth and authorization middleware, and scope every injected port to the request subject.
4508
+
4509
+ ### Browser views and the Workbench
4510
+
4511
+ `@nifrajs/agent-app` is a backend-free browser SDK for negotiated commands, ordered
4512
+ and resumable event streams, approvals, handoffs, and Run Studio projections. Its public views
4513
+ contain structure - statuses, counters, coordinates, and error codes - rather than prompts,
4514
+ tool payloads, model output, or filesystem paths. The optional Workbench uses that same surface:
4515
+ run `bun run --filter '@nifrajs/workbench' dev -- --cwd /path/to/project` for the
4516
+ local browser client.
4517
+
4518
+ ```ts
4519
+ import { AgentAppClient, HttpAgentTransport } from "@nifrajs/agent-app"
4520
+
4521
+ declare function currentToken(): string
4522
+ declare function render(view: unknown): void
4523
+
4524
+ const client = new AgentAppClient(new HttpAgentTransport({
4525
+ endpoint: "http://127.0.0.1:8787",
4526
+ authorize: () => currentToken(), // minted per request; never stored by the transport
4527
+ }))
4528
+
4529
+ const session = await client.createSession()
4530
+ for await (const view of client.send("summarize the diff")) {
4531
+ render(view) // content-free status, counts, coordinates, and error codes
4532
+ }
4533
+ ```
4534
+
4535
+ ### Evidence, streaming, and telemetry
4536
+
4537
+ `@nifrajs/agent` runs bounded typed turns with tool contracts, budgets, approvals,
4538
+ resumable token-only evidence, model deltas, and a transient shared-state channel. Add an
4539
+ `evidenceLog` to the HTTP seams for SSE replay after a dropped connection; replay
4540
+ resumes evidence and never re-executes the turn. `@nifrajs/agent-telemetry` converts
4541
+ the same constrained evidence into fail-open OpenTelemetry run and step spans. Providers,
4542
+ credentials, durable storage, retention, and tenant policy remain application-owned ports.
4543
+
4012
4544
  ### Projects without a web config
4013
4545
 
4014
4546
  A backend-only project - the shape `create-nifra`'s default template produces - has
@@ -4654,9 +5186,9 @@ global state; a second copy nulls it out and every hook throws.
4654
5186
 
4655
5187
  Nifra **dedupes React** in both the production build and the Vite dev server, so
4656
5188
  the framework itself won't load two copies. The usual culprit is a
4657
- **`file:`-linked package** (a local component library you
4658
- `bun link` or reference with `file:../lib`) that bundles its own React in
4659
- its own `node_modules`:
5189
+ **`file:`- or `link:`-linked package** (a local component
5190
+ library you `bun link` or reference with `link:../lib`) that bundles its
5191
+ own React in its own `node_modules`. Fix the install first, if you can:
4660
5192
 
4661
5193
  -
4662
5194
  Make React a **peer dependency** of the linked package (not a regular
@@ -4672,6 +5204,70 @@ copy.
4672
5204
  Delete the linked package's nested `node_modules/react` after linking if your
4673
5205
  package manager duplicated it.
4674
5206
 
5207
+ #### When the duplicate can't be installed away: declare it single-copy
5208
+
5209
+ A `link:` dependency on a **separate checkout** is the case none of the
5210
+ above reaches. The linked package's files live in the other repo, so Node and Bun resolve their
5211
+ imports from _that_ repo's real path - and that repo's install owns its
5212
+ `node_modules`. Peer deps don't help (the copy is already installed there),
5213
+ `overrides` don't help (they govern your install, not theirs), and deleting the
5214
+ nested copy is undone by the next install in the sibling repo. Only a resolver can answer this:
5215
+ the walk has to be intercepted, not rearranged.
5216
+
5217
+ Declare the packages in your app's `package.json`. It is a **static**
5218
+ declaration on purpose - `nifra check` reads it without importing your config, so
5219
+ the preflight never executes app code:
5220
+
5221
+ ```ts
5222
+ {
5223
+ "name": "my-app",
5224
+ "dependencies": {
5225
+ "react": "19.2.8",
5226
+ "@example/ui": "link:../design-system/packages/ui"
5227
+ },
5228
+ "nifra": {
5229
+ "singleCopy": ["react", "react-dom", "@nifrajs/*"]
5230
+ }
5231
+ }
5232
+ ```
5233
+
5234
+ `"singleCopy": true` is shorthand for Nifra's built-in identity-sensitive set:
5235
+ `@nifrajs/*`, `react`, `react-dom`, `preact`,
5236
+ `solid-js`, `svelte`, `vue`. Entries may be exact names or a
5237
+ `@scope/*` pattern. `@nifrajs/*` belongs in this set for the same reason
5238
+ React does: two copies of `@nifrajs/core` are two distinct `Server`
5239
+ classes, so `.merge()` stops accepting an app built against the other one.
5240
+
5241
+ **The build honours the declaration on its own** -
5242
+ `buildClient` and `buildServer` inject the resolver, so bundled output
5243
+ already loads one copy. Unbundled phases do not: Bun's _runtime_ resolver never offers a
5244
+ bare specifier to a plugin, so `bun test`, `bun run`, and preloaded
5245
+ scripts still load both copies unless you preload the registrar:
5246
+
5247
+ ```ts
5248
+ # bunfig.toml
5249
+ preload = ["@nifrajs/core/single-copy/register"]
5250
+
5251
+ [test]
5252
+ preload = ["@nifrajs/core/single-copy/register"]
5253
+ ```
5254
+
5255
+ Both sections matter, and they are independent: `preload` covers
5256
+ `bun run`, `[test].preload` covers `bun test`. Declaring
5257
+ without preloading is a real state, not a mistake -
5258
+ `nifra check` reports it and names the phase left uncovered.
5259
+
5260
+ **What it will not do:** redirect across _versions_. If the two copies are
5261
+ `19.2.7` and `19.2.8`, the redirect is skipped and
5262
+ `nifra check` still fails with `version-skew`. Silently collapsing a
5263
+ version difference would trade a loud install problem for a quiet behavioural one; align the
5264
+ ranges instead.
5265
+
5266
+ A declared duplicate is **reported, not suppressed**.
5267
+ `nifra check` keeps printing the copies as a warning and
5268
+ `nifra doctor` lists them under "deduplicated by declaration", so the topology stays
5269
+ visible - it just stops being fatal.
5270
+
4675
5271
  [!NOTE]
4676
5272
 
4677
5273
  This applies to every framework with module-global render state, not just React (Preact,
@@ -5339,9 +5935,9 @@ export const hydrate = false // opt out of full-document hydration (static / i
5339
5935
  export default function User(props) { /* props.data, props.actionData, props.params */ }
5340
5936
  ```
5341
5937
 
5342
- [!NOTE] An `action` may return a `Response` (a `redirect()`,
5343
- say) - it's passed through untouched. Any other return is serialized as
5344
- `actionData`.
5938
+ [!NOTE] An `action` may return a control-flow value - a `redirect()` or a
5939
+ `status(...)` render, or a hand-rolled `Response` - and it's passed
5940
+ through untouched. Any other return is serialized as `actionData`.
5345
5941
 
5346
5942
  ### Two ways to handle a request
5347
5943
 
@@ -6434,6 +7030,94 @@ If capabilities are configured, review a new snapshot and run
6434
7030
  Exercise authentication, redirects, backend mounting, SSE/WebSocket routes, and each deploy
6435
7031
  adapter the application uses.
6436
7032
 
7033
+ ## Nifra 3.0 migration guide - upgrade from 2.x
7034
+
7035
+ > Upgrade a Nifra 2.x application to 3.0: automated dependency pins, the plain-data redirect return value, the frozen reserved typed-client segment keys and their codemod, the Node proxy transport default, and the release gates.
7036
+
7037
+ Nifra 3.0 makes a redirect the same plain-data value an ordinary return is, and freezes the
7038
+ reserved typed-client segment keys into a published contract. The upgrade command handles the
7039
+ deterministic dependency pins; this guide covers the two structural changes it deliberately
7040
+ cannot guess, plus the Node proxy transport default.
7041
+
7042
+ ### 1. Run the executable upgrade
7043
+
7044
+ ```ts
7045
+
7046
+ ```
7047
+
7048
+ The command pins every matching `@nifrajs/*`, `Nifra`, and
7049
+ `create-nifra` dependency to `3.0.0` while preserving
7050
+ caret/tilde/exact style. No package is removed and no import specifier moves this release, so
7051
+ the runner only pins. Dry-run is the default; `--write` applies and verifies with
7052
+ `nifra check`.
7053
+
7054
+ ### 2. Read redirects as plain data
7055
+
7056
+ `redirect(...)` now returns the same plain render value `status(...)`
7057
+ produces, not a Web `Response`. Same bytes on the wire, now with a
7058
+ `content-length` on Node instead of a chunked empty body. It is still returned or
7059
+ thrown from the same places - loader, action, layout gate - and `return redirect()`
7060
+ / `throw redirect()` stay interchangeable, including the client-submit conversion to
7061
+ a 204 with `X-Nifra-Redirect`.
7062
+
7063
+ **Breaking:** the value is no longer a `Response`, so
7064
+ `.status`, `.headers`, and `instanceof Response` are gone from
7065
+ it. Read `.plain`, build one with `toResponse()` when needed, pass headers
7066
+ through the second argument, and assert on `.plain` in tests. A hand-rolled
7067
+ `Response` from a loader or action is untouched - only what `redirect()`
7068
+ itself returns changed.
7069
+
7070
+ ```ts
7071
+
7072
+ ```
7073
+
7074
+ ### 3. Fix reserved typed-client segments
7075
+
7076
+ The reserved proxy keys are now a frozen, published contract. A route whose static path
7077
+ segment spells one of them - an HTTP verb in any casing, or `subscribe`,
7078
+ `ws`, `index`, `then` - is unreachable by property access,
7079
+ because the proxy resolves the reserved key before a path segment. The types now reject that
7080
+ access at compile time instead of silently reaching the wrong node.
7081
+
7082
+ ```ts
7083
+
7084
+ ```
7085
+
7086
+ Run `nifra fix --code NF-C018` to rewrite the affected call sites. It reads them
7087
+ from the compiler rather than a text search, so it finds every one and never mistakes a real
7088
+ `.delete` verb call for a path segment; a site it cannot rewrite confidently
7089
+ (bracket access, a node held in a variable) is reported and left alone.
7090
+ `nifra routes` annotates a colliding route with the spelling that reaches it, in
7091
+ both the table and `--json`. The set is exported as the one place it is written down.
7092
+
7093
+ ```ts
7094
+
7095
+ ```
7096
+
7097
+ ### 4. Review the Node proxy transport (only if you use it)
7098
+
7099
+ On Node, `@nifrajs/proxy` now defaults to the undici transport, shipped from the new
7100
+ `@nifrajs/proxy/undici` subpath. Pass an explicit `transport` to
7101
+ `createProxy` to override it.
7102
+
7103
+ ```ts
7104
+
7105
+ ```
7106
+
7107
+ ### 5. Run the release gates
7108
+
7109
+ -
7110
+ Run `nifra check --json` and fix every error, including any remaining NF-C018.
7111
+
7112
+ -
7113
+ If the project has `nifra.assurance.ts`, run `nifra assure --json`.
7114
+
7115
+ - Run the application test suite and a production build.
7116
+
7117
+ -
7118
+ Exercise every redirect path - loader, action, and layout gate - and, if the app proxies,
7119
+ each deploy adapter it uses.
7120
+
6437
7121
  ## Nifra - Migrating from Express, Hono, Fastify & Elysia
6438
7122
 
6439
7123
  > Move a backend to Nifra: routing, middleware, validation, and body parsing map directly from Express, Hono, Fastify, and Elysia - and you gain a typed client, multi-runtime deploy, and optional SSR.
@@ -7495,26 +8179,278 @@ level that regressed comes back with the reason it broke.
7495
8179
 
7496
8180
  # Package reference (READMEs)
7497
8181
 
7498
- ## @nifrajs/agent
8182
+ ## @nifrajs/a2a
7499
8183
 
7500
- Provider-neutral, typed, resumable agent turns over Nifra tool contracts.
8184
+ 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.
7501
8185
 
7502
- The package owns one bounded turn at a time. Model providers, state, approvals, budgets, idempotency,
7503
- and telemetry are ports. The package never imports a model vendor and its state adapter stores only
7504
- token-only turn evidence.
8186
+ 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.
7505
8187
 
7506
- ```ts
7507
- import { createAgentState, runAgent } from "@nifrajs/agent"
8188
+ ## Install
7508
8189
 
7509
- const result = await runAgent(definition, { value: input }, ports, {
7510
- state: createAgentState("support-1"),
7511
- maxTurns: 8,
7512
- })
8190
+ ```bash
8191
+ bun add @nifrajs/a2a @nifrajs/agent
8192
+ ```
8193
+
8194
+ ## Mount
8195
+
8196
+ ```ts
8197
+ import { server } from "@nifrajs/core"
8198
+ import { mountA2A } from "@nifrajs/a2a"
8199
+ import { agent } from "./agent" // an AgentDefinition
8200
+
8201
+ const app = server()
8202
+ mountA2A(app, {
8203
+ agent,
8204
+ card: { url: "https://api.example.com/a2a", version: "1.0.0" },
8205
+ ports: (c) => ({
8206
+ model: myModelPort, // scope to the request subject
8207
+ capabilities: ["search.read"],
8208
+ state: myStateStore, // required for GetTask and resume
8209
+ }),
8210
+ })
8211
+ // GET /.well-known/agent-card.json - the agent card
8212
+ // POST /a2a - SendMessage | SendStreamingMessage | GetTask
8213
+ ```
8214
+
8215
+ ## Task mapping
8216
+
8217
+ One A2A task is one agent turn: `taskId` is the runner's `turnId`.
8218
+
8219
+ | Run result | Task state |
8220
+ | --- | --- |
8221
+ | completed with output | `TASK_STATE_COMPLETED` + an `output` artifact |
8222
+ | completed with a typed error | `TASK_STATE_FAILED` (error in `metadata`) |
8223
+ | suspended (approval, budget, model, max turns) | `TASK_STATE_INPUT_REQUIRED` |
8224
+ | suspended (cancelled) | `TASK_STATE_CANCELED` |
8225
+
8226
+ Agent input is `message.metadata.input` when present, otherwise the first `text` part. Structured output returns as a `text` part carrying JSON.
8227
+
8228
+ ## Human-in-the-loop resume
8229
+
8230
+ 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`:
8231
+
8232
+ ```jsonc
8233
+ {
8234
+ "jsonrpc": "2.0", "id": 2, "method": "SendMessage",
8235
+ "params": {
8236
+ "message": {
8237
+ "messageId": "m2", "role": "ROLE_USER", "parts": [],
8238
+ "taskId": "<task id from the suspension>",
8239
+ "metadata": {
8240
+ "input": { "prompt": "go" },
8241
+ "resume": {
8242
+ "continuation": { "kind": "approval", "tool": "search.read", "effectId": "…", "input": { "q": "…" } },
8243
+ "approval": { "granted": true }
8244
+ }
8245
+ }
8246
+ }
8247
+ }
8248
+ }
8249
+ ```
8250
+
8251
+ Cross-request registries (cancellation, subscription fan-out, push notifications) are deliberately out of scope for the stateless seam; those spec methods answer `UnsupportedOperationError` (-32004).
8252
+
8253
+ 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.
8254
+
8255
+ ## Docs
8256
+
8257
+ 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`.
8258
+
8259
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8260
+ [`../../llms-full.txt`](../../llms-full.txt).
8261
+
8262
+ ## @nifrajs/ag-ui
8263
+
8264
+ 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.
8265
+
8266
+ 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.
8267
+
8268
+ ## Install
8269
+
8270
+ ```bash
8271
+ bun add @nifrajs/ag-ui @nifrajs/agent
8272
+ ```
8273
+
8274
+ ## Mount
8275
+
8276
+ ```ts
8277
+ import { server } from "@nifrajs/core"
8278
+ import { mountAgUI } from "@nifrajs/ag-ui"
8279
+ import { agent } from "./agent" // an AgentDefinition
8280
+
8281
+ const app = server()
8282
+ mountAgUI(app, {
8283
+ agent,
8284
+ ports: (c) => ({
8285
+ model: myModelPort, // scope to the request subject
8286
+ capabilities: ["search.read"],
8287
+ state: myStateStore, // required for resume
8288
+ }),
8289
+ })
8290
+ // POST /agui - RunAgentInput in, AG-UI events out (SSE)
8291
+ ```
8292
+
8293
+ ## Event mapping
8294
+
8295
+ | Runner | AG-UI events |
8296
+ | --- | --- |
8297
+ | run starts | `RUN_STARTED`, then `CUSTOM { name: "nifra.turn" }` announcing the turn id |
8298
+ | tool evidence | `TOOL_CALL_START` / `TOOL_CALL_END`, then `TOOL_CALL_RESULT` whose `content` is the token-only outcome `{ "outcome": "committed" \| "failed" \| "denied", "code"? }` |
8299
+ | model, approval, budget, state evidence | `STEP_STARTED` / `STEP_FINISHED` |
8300
+ | model text deltas | live `TEXT_MESSAGE_START` / `_CONTENT` (see streaming below) |
8301
+ | model reasoning deltas | `REASONING_START` / `REASONING_MESSAGE_START` / `_CONTENT` / `_END` / `REASONING_END` |
8302
+ | model tool-args deltas | provisional `TOOL_CALL_START` + `TOOL_CALL_ARGS`, closed by the call's tool evidence |
8303
+ | shared state | `STATE_SNAPSHOT` on the seed and `STATE_DELTA` (RFC 6902 ops) per patch (see below) |
8304
+ | model usage deltas | summed per `(provider, model)` into the `usage: TokenUsage[]` array on `RUN_FINISHED` |
8305
+ | 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" }` |
8306
+ | completed with a typed error | `RUN_ERROR` |
8307
+ | suspended | `CUSTOM { name: "nifra.pending" }` with the continuation, then `RUN_FINISHED` with `outcome: { "type": "interrupt", "interrupts": [...] }` |
8308
+
8309
+ 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.
8310
+
8311
+ Agent input is `forwardedProps.input` when present, otherwise the content of the last `role: "user"` message.
8312
+
8313
+ ## Token streaming
8314
+
8315
+ A model port that calls the request's optional `onDelta` callback (see `@nifrajs/agent`'s `AgentModelDelta`) streams live:
8316
+
8317
+ - `{ 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.
8318
+ - `{ kind: "reasoning", text }` streams a `REASONING_*` message.
8319
+ - `{ 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.
8320
+ - `{ 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.
8321
+
8322
+ 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`.
8323
+
8324
+ ## Shared state
8325
+
8326
+ 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.
8327
+
8328
+ ```ts
8329
+ mountAgUI(app, {
8330
+ agent,
8331
+ ports: (c, run) => ({
8332
+ model: myStreamingModelPort(run.sharedState), // patch progress as the run advances
8333
+ capabilities: ["search.read"],
8334
+ }),
8335
+ })
8336
+ ```
8337
+
8338
+ 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.
8339
+
8340
+ ## Human-in-the-loop resume
8341
+
8342
+ A suspended run finishes with `outcome: { "type": "interrupt", "interrupts": [interrupt] }` where the interrupt is:
8343
+
8344
+ ```jsonc
8345
+ {
8346
+ "id": "<turnId>",
8347
+ "reason": "approval", // approval | budget | model | cancelled | max_turns
8348
+ "toolCallId": "<effectId>", // present for tool suspensions
8349
+ "responseSchema": { /* JSON Schema for the resume payload */ },
8350
+ "metadata": { "turnId": "…", "continuation": { "kind": "approval", "tool": "…", "effectId": "…" } }
8351
+ }
8352
+ ```
8353
+
8354
+ 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`:
8355
+
8356
+ ```jsonc
8357
+ {
8358
+ "threadId": "thread-1", "runId": "run-2", "messages": [],
8359
+ "forwardedProps": { "input": { "prompt": "go" } },
8360
+ "resume": [{
8361
+ "interruptId": "<interrupt id>",
8362
+ "status": "resolved", // "cancelled" without an approval resumes as a denial
8363
+ "payload": {
8364
+ "continuation": { "kind": "approval", "tool": "search.read", "effectId": "…", "input": { "q": "…" } },
8365
+ "approval": { "granted": true }
8366
+ }
8367
+ }]
8368
+ }
8369
+ ```
8370
+
8371
+ 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.
8372
+
8373
+ ## Resumable streams
8374
+
8375
+ Pass an `evidenceLog` to make a dropped SSE connection resumable. Evidence-derived frames then
8376
+ carry SSE `id: <seq>`; a client reconnects by re-POSTing the same body with a `Last-Event-ID`
8377
+ header and receives the missed events, rejoining a still-running turn live or replaying the stored
8378
+ terminal events - the run is never re-executed.
8379
+
8380
+ ```ts
8381
+ import { createMemoryAgentEvidenceLog } from "@nifrajs/agent/events"
8382
+
8383
+ mountAgUI(app, { agent, ports, evidenceLog: createMemoryAgentEvidenceLog() })
8384
+ ```
8385
+
8386
+ The in-memory log is the single-process dev/test reference; a durable, multi-process log is an
8387
+ adapter implementing the same `AgentEvidenceLog` interface.
8388
+
8389
+ 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.
8390
+
8391
+ ## Docs
8392
+
8393
+ 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`.
8394
+
8395
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8396
+ [`../../llms-full.txt`](../../llms-full.txt).
8397
+
8398
+ ## @nifrajs/agent
8399
+
8400
+ Provider-neutral, typed, resumable agent turns over Nifra tool contracts.
8401
+
8402
+ The package owns one bounded turn at a time. Model providers, state, approvals, budgets, idempotency,
8403
+ and telemetry are ports. The package never imports a model vendor and its state adapter stores only
8404
+ token-only turn evidence.
8405
+
8406
+ ```ts
8407
+ import { createAgentState, runAgent } from "@nifrajs/agent"
8408
+
8409
+ const result = await runAgent(definition, { value: input }, ports, {
8410
+ state: createAgentState("support-1"),
8411
+ maxTurns: 8,
8412
+ })
7513
8413
  ```
7514
8414
 
7515
8415
  The public in-memory adapters are for local development and tests. Durable state, provider
7516
8416
  credentials, and operated policy remain adapter concerns.
7517
8417
 
8418
+ Gateway and deployment contracts are deliberately provider-neutral. Retry, fallback, budget,
8419
+ deadline, workspace, and hostile-code isolation claims are admitted only from a host-approved
8420
+ capability report. The local and replay reference profiles are not hostile-code sandboxes, and no
8421
+ public package loads provider credentials or promises durable execution.
8422
+
8423
+ Several telemetry consumers can observe the same run: `combineAgentTelemetry(a, b, ...)` fans step
8424
+ evidence out to every port in order, so an SSE evidence stream and an exporter (for example
8425
+ `@nifrajs/agent-telemetry`'s `traceAgentRun`) compose instead of displacing each other. The HTTP
8426
+ seams (`mountAgent`, `@nifrajs/a2a`, `@nifrajs/ag-ui`) compose their streaming evidence with any
8427
+ telemetry port supplied through `ports`.
8428
+
8429
+ A model port can stream: when the caller wires an `AgentDeltaSink` into `ports.deltas`, every model
8430
+ request carries an optional `onDelta` callback the port may call per chunk - user-visible text,
8431
+ reasoning text, the raw argument text of the tool call being formed, or a `usage` delta reporting
8432
+ the decision's settled token counts (optionally attributed to a provider and model, for observers
8433
+ to sum across the run). Deltas are transient
8434
+ observer data for live UIs (`@nifrajs/ag-ui` projects them onto `TEXT_MESSAGE_*`, `REASONING_*`,
8435
+ and `TOOL_CALL_ARGS` frames): they are never validated, persisted, or replayed, and a failing sink
8436
+ never fails the model step. `combineAgentDeltaSinks(a, b, ...)` fans deltas out the way
8437
+ `combineAgentTelemetry` fans evidence.
8438
+
8439
+ `createAgentSharedState(initial)` is the run's shared UI state channel: a JSON document any port or
8440
+ tool executor patches with RFC 6902 operations (`add`, `replace`, `remove`), with subscribers
8441
+ observing every applied batch. Patches apply atomically - an invalid op rejects the whole batch -
8442
+ and prototype-grafting pointer segments are refused. The document is per-run observer data, not
8443
+ turn state: nothing in it is persisted by the runtime. `@nifrajs/ag-ui` projects the channel onto
8444
+ AG-UI `STATE_SNAPSHOT`/`STATE_DELTA` events.
8445
+
8446
+ SSE streams are resumable: give `mountAgent` (or `@nifrajs/ag-ui`'s `mountAgUI`) an `evidenceLog`
8447
+ and `step` frames carry `id: <seq>`. A client that loses the connection re-POSTs the same `turnId`
8448
+ with a `Last-Event-ID` header and receives the missed evidence, rejoining a still-running turn live
8449
+ or replaying the stored terminal frame - the run is never re-executed.
8450
+ `createMemoryAgentEvidenceLog` (from `@nifrajs/agent/events`) is the single-process dev/test
8451
+ reference; a durable, multi-process log is an adapter implementing the same `AgentEvidenceLog`
8452
+ interface.
8453
+
7518
8454
  Execution policies can be required by a tool contract. `createLocalProcessAdapter` applies cwd and
7519
8455
  environment filtering, timeouts, and cancellation to child processes. The local adapter is NOT a
7520
8456
  security boundary. Without OS-level sandboxing it contains crashes and accidents, not hostile code.
@@ -7522,6 +8458,111 @@ security boundary. Without OS-level sandboxing it contains crashes and accidents
7522
8458
  For the compact agent contract, see [`LLM.md`](./LLM.md). The full machine-readable corpus is
7523
8459
  [`../../llms-full.txt`](../../llms-full.txt).
7524
8460
 
8461
+ ## @nifrajs/agent-app
8462
+
8463
+ A presentation-safe browser SDK for Nifra agent hosts.
8464
+
8465
+ This package depends on **only** `@nifrajs/agent-protocol`. It pulls in no backend, model provider,
8466
+ storage, or UI framework, so it can be bundled into browser-facing code without dragging a private
8467
+ engine or payload content across the boundary. Everything it surfaces upward is a content-free view
8468
+ model: identifiers, lifecycle statuses, counters, and opaque references. A prompt becomes a character
8469
+ count; a tool result becomes an `ok` flag and an error *code*; a compaction becomes before/after token
8470
+ counts.
8471
+
8472
+ ## What it does
8473
+
8474
+ - **Feature negotiation.** On session creation the client intersects the features it can drive with the
8475
+ capabilities the host advertises, and gates the matching operations behind `requireFeature`.
8476
+ - **Caller auth.** An optional `authorize` hook mints a bearer token per request. The token is placed
8477
+ on the outgoing `Authorization` header and never stored on the transport, copied into a result, or
8478
+ written to a log.
8479
+ - **Ordered, deduplicated streaming.** A turn arrives as an ordered stream of `AgentEventView`s.
8480
+ Out-of-order and redelivered frames are reordered and de-duplicated by sequence; a bounded buffer
8481
+ skips an unfilled gap rather than stalling.
8482
+ - **Cursor resume.** A persisted log can be replayed from a cursor. A cursor whose next record was
8483
+ evicted from the retained window asks the caller to resync.
8484
+ - **Approvals and handoffs.** Pending approvals list as identifiers; decisions and handoff outcomes go
8485
+ back through negotiated commands.
8486
+ - **Run Studio views.** Run graphs, retry/recovery timelines, eval comparisons, and fault-injection
8487
+ rows consume only protocol evidence and bounded replay references.
8488
+ - **Escape hatch.** `command()` reaches host-specific surfaces outside the negotiated contract and
8489
+ still returns a bounded `CommandOutcome` - it never throws the credential.
8490
+
8491
+ ## Usage
8492
+
8493
+ ```ts
8494
+ import { AgentAppClient, HttpAgentTransport } from "@nifrajs/agent-app"
8495
+
8496
+ const transport = new HttpAgentTransport({
8497
+ endpoint: "http://127.0.0.1:8787",
8498
+ authorize: () => currentToken(), // per-request; never stored
8499
+ })
8500
+ const client = new AgentAppClient(transport)
8501
+
8502
+ const session = await client.createSession()
8503
+ if (client.supports("resume")) {
8504
+ const replay = await client.resume({ cursor: lastCursor })
8505
+ // replay.entries: { seq, at, type }[] - no payloads
8506
+ }
8507
+
8508
+ for await (const view of client.send("summarize the diff")) {
8509
+ // view is content-free: e.g. { kind: "assistant.delta", seq, chars }
8510
+ }
8511
+ ```
8512
+
8513
+ ## Boundary
8514
+
8515
+ The transport receives the host's full event stream in order to derive counts, but the client's public
8516
+ output is projected to content-free view models. That projection is the seam: an application built on
8517
+ this SDK can render progress and resolve interactions, and cannot reconstruct a prompt, tool payload,
8518
+ model completion, diagnostic report, or filesystem path. The `scripts/check-agent-boundary.ts` gate
8519
+ enforces that this package imports nothing but `@nifrajs/agent-protocol`.
8520
+
8521
+ The SDK is transport-neutral and opt-in: it does not retain payloads, provide identity or tenancy,
8522
+ claim a remote security boundary, or imply durable delivery. Production hosts own authorization,
8523
+ retention, and adapter policy.
8524
+
8525
+ ## License
8526
+
8527
+ MIT
8528
+
8529
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8530
+ [`../../llms-full.txt`](../../llms-full.txt).
8531
+
8532
+ ## @nifrajs/agent-protocol
8533
+
8534
+ Small, backend-neutral contracts for local coding-agent sessions.
8535
+
8536
+ The package contains versioned session snapshots, streamed assistant/tool/approval
8537
+ events, bounded live event streams, and backend interfaces. It has no runtime
8538
+ dependencies and does not import Nifra framework packages, model SDKs, or UI code.
8539
+
8540
+ ```ts
8541
+ import { createAgentEventStream } from "@nifrajs/agent-protocol"
8542
+
8543
+ const events = createAgentEventStream(256)
8544
+ events.push(/* a versioned AgentEvent */)
8545
+ ```
8546
+
8547
+ Use `@nifrajs/pi` for the Pi backend or implement `AgentBackend` for another
8548
+ runtime. The protocol is intentionally small so CLI, desktop, mobile, and CI
8549
+ clients can share it without pulling in the Nifra application framework.
8550
+
8551
+ ## Version 1 compatibility
8552
+
8553
+ Version 1 is additive. Hosts advertise capabilities and clients use only the negotiated intersection;
8554
+ an unsupported command is a stable `feature_unsupported` result. Optional fields are ignored by older
8555
+ peers, while a cursor gap returns `resync_required` instead of silently skipping evidence. A future
8556
+ major requires a recorded semantic incompatibility, dual decoders, cross-version fixtures, and an
8557
+ approved migration plan.
8558
+
8559
+ Run evidence and lifecycle values are structural only: identifiers, statuses, counters, timestamps,
8560
+ digests, and opaque references. Prompts, model output, tool arguments, and filesystem paths do not
8561
+ belong in these public contracts.
8562
+
8563
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
8564
+ [`../../llms-full.txt`](../../llms-full.txt).
8565
+
7525
8566
  ## @nifrajs/agent-telemetry
7526
8567
 
7527
8568
  Child-span telemetry for AI tool calls made through Nifra tool and MCP endpoints.
@@ -7540,6 +8581,35 @@ Requests under `/_nifra/tool/*` and requests to `/mcp` receive tool-name, input-
7540
8581
  duration, and status observations. When `@nifrajs/otel` owns the request observation, tool spans are
7541
8582
  attached as children; otherwise the plugin creates a standalone observation. Other routes pass through.
7542
8583
 
8584
+ ## Tracing agent runs
8585
+
8586
+ `traceAgentRun` turns a `@nifrajs/agent` run's step evidence into an OpenTelemetry trace: one
8587
+ `nifra.agent.run` span per run, one child span per evidence item, with tool names, effect ids, error
8588
+ codes, and effect-ledger heads carried as span attributes. Only the runner's constrained evidence
8589
+ fields are exported - prompts, tool inputs, and outputs cannot enter a trace.
8590
+
8591
+ Orchestration correlation is opt-in and cardinality-bounded. Allowed attributes are run, plan, node,
8592
+ attempt, evidence, replay, and trace coordinates plus bounded status/error codes. Content-bearing or
8593
+ unbounded attributes are rejected; the exporter is not a payload retention sink.
8594
+
8595
+ ```ts
8596
+ import { traceAgentRun } from "@nifrajs/agent-telemetry"
8597
+ import { combineAgentTelemetry, runAgent } from "@nifrajs/agent"
8598
+
8599
+ const trace = traceAgentRun({ agent: agent.name, turnId, exporter })
8600
+ const result = await runAgent(agent, input, {
8601
+ ...ports,
8602
+ telemetry: combineAgentTelemetry(ports.telemetry, trace.telemetry),
8603
+ })
8604
+ trace.end(result)
8605
+ ```
8606
+
8607
+ Model evidence arrives as a started/terminal pair and becomes one timed `nifra.agent.model` span;
8608
+ tool, approval, budget, and state evidence are terminal-only and become instant children. Pass
8609
+ `parent` (for example the enclosing request observation's `context`) to nest runs under the HTTP
8610
+ span, or `traceparent` to continue an inbound W3C trace; `trace.context` is the run span's identity,
8611
+ forwardable with `traceHeaders`. Telemetry is fail-open: a throwing exporter never fails the turn.
8612
+
7543
8613
  ## For AI agents
7544
8614
 
7545
8615
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -7691,8 +8761,8 @@ app.get("/me", async (c) => {
7691
8761
 
7692
8762
  ## Protect a route
7693
8763
 
7694
- `requireSession(auth, request, options?)` returns the non-null session or **throws a `Response`** -
7695
- nifra returns a thrown `Response` as-is, short-circuiting the handler:
8764
+ `requireSession(auth, request, options?)` returns the non-null session or **throws a Nifra `ResponseResult`** -
8765
+ nifra handles the thrown `ResponseResult` as control flow, short-circuiting the handler:
7696
8766
 
7697
8767
  ```ts
7698
8768
  import { requireSession } from "@nifrajs/better-auth"
@@ -7716,7 +8786,7 @@ export const loader = async ({ request }) => {
7716
8786
  | --- | --- |
7717
8787
  | `betterAuth(auth, options?)` | Plugin that mounts better-auth's handler at `${basePath}/*` for GET + POST. |
7718
8788
  | `getSession(auth, request)` | `Promise<SessionOf<A> \| null>` - typed wrapper over `auth.api.getSession`. |
7719
- | `requireSession(auth, request, options?)` | Returns the session or throws a `Response` (302 `redirectTo` / 401). |
8789
+ | `requireSession(auth, request, options?)` | Returns the session or throws a Nifra `ResponseResult` (302 `redirectTo` / 401). |
7720
8790
  | `SessionOf<A>` | The non-null session payload type inferred from your `auth`. |
7721
8791
  | `BetterAuthLike` | The structural contract a better-auth instance satisfies. |
7722
8792
 
@@ -7770,8 +8840,9 @@ await cache.invalidateTag(`user:${id}`)
7770
8840
 
7771
8841
  ## Stores
7772
8842
 
7773
- The default `MemoryCache` is in-process with lazy expiry, a tag index, and an optional LRU cap
7774
- (`new MemoryCache({ maxEntries: 10_000 })`). For a cache shared across instances, implement `CacheStore`
8843
+ The default `MemoryCache` is in-process with incremental expiry, a tag index, and a 10,000-entry LRU
8844
+ cap. Tune it with `new MemoryCache({ maxEntries: 2_000 })`; `maxEntries: 0` is the explicit unbounded
8845
+ opt-in. For a cache shared across instances, implement `CacheStore`
7775
8846
  (`get` / `set` / `delete` / `invalidateTag` / `clear`) over CF KV, Redis, etc.:
7776
8847
 
7777
8848
  ```ts
@@ -7808,6 +8879,7 @@ nifra start [--port <n>] [--out <dir>] Run the generated Bun server at <out>
7808
8879
  nifra context Print this project's API + page routes + conventions.
7809
8880
  nifra mcp MCP server (stdio) exposing this project to a coding agent.
7810
8881
  nifra check [--json] Gate: typecheck + lints; --json includes fixes/suggestions.
8882
+ nifra migrate --from tailwind --to stylex Dry-run safe static Tailwind → StyleX migration; --write applies it.
7811
8883
  nifra assure [--config <file>] [--json] Gate reflected routes against enforcement evidence.
7812
8884
  nifra capabilities check [--json] Gate effect provenance + the reviewed capability lockfile.
7813
8885
  nifra manifest emit [--sign <key-ref>] Emit the hash-verified route trust artifact after assurance.
@@ -7870,6 +8942,15 @@ For hand-rolled own-API `fetch()` calls, `nifra check` stays conservative: simpl
7870
8942
  that match a statically visible Nifra route get an exact typed-client rewrite diff; dynamic URLs, custom
7871
8943
  headers, bodies, query strings, or ambiguous routes fall back to manual steps.
7872
8944
 
8945
+ ## `nifra migrate --from tailwind --to stylex`
8946
+
8947
+ This command is a dry run by default. It rewrites static `className` attributes whose utilities have a
8948
+ direct StyleX equivalent into `stylex.props(...)` plus a local `stylex.create(...)` table. Use
8949
+ `--write` to apply the safe changes, and `--dir` to scan a project subdirectory. Responsive breakpoints
8950
+ and element-local pseudo-classes are supported; dynamic expressions, arbitrary values, parent-dependent
8951
+ variants, and unknown utilities are left untouched and reported with file and line evidence. A non-zero
8952
+ exit status means manual migration issues remain.
8953
+
7873
8954
  When a library such as Better Auth intentionally owns a mounted prefix outside the Nifra contract,
7874
8955
  declare that prefix in `nifra.check.json` so its relative calls do not keep CI red:
7875
8956
 
@@ -7996,6 +9077,76 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
7996
9077
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
7997
9078
  done-gate, or `nifra mcp` to give the agent live project tools.
7998
9079
 
9080
+ ## @nifrajs/coding-agent
9081
+
9082
+ Standalone, extensible Nifra coding-agent host and CLI.
9083
+
9084
+ The package provides `nifra-agent` in interactive, one-shot, JSON, and local
9085
+ loopback RPC modes. It adds sessions, checkpoints, bounded compaction,
9086
+ transactional TypeScript extensions, self-healing repair stages, workflows,
9087
+ bounded subagents, and Nifra verification descriptors without changing Nifra's
9088
+ framework runtime.
9089
+
9090
+ Extensions can register bounded workflows that are listed and run through the
9091
+ authenticated RPC surface. Capability manifests, project workspace policies,
9092
+ optional isolated-worktree leases, and a pre-activation syntax gate keep those
9093
+ customizations explicit and replaceable.
9094
+
9095
+ ```sh
9096
+ bunx nifra-agent --backend pi
9097
+ bunx nifra-agent --backend pi --message "run the checks and explain failures"
9098
+ bunx nifra-agent --backend pi --verify-after-turn check --max-repair-attempts 2
9099
+ bunx nifra-agent --backend replay --replay ./session-events.jsonl --json
9100
+ ```
9101
+
9102
+ When --verify-after-turn is enabled, a failed gate becomes a bounded repair turn: the host sends
9103
+ the failure diagnostics back to the agent, runs the gate again, and stops on success or after the
9104
+ configured attempt cap. Use --max-repair-attempts 0 for observe-only verification.
9105
+
9106
+ For embedding, use `CodingAgentHost` or `CodingAgentRpcServer`. The host keeps
9107
+ live event queues bounded, filters verification subprocess environments, and
9108
+ requires explicit local RPC authorization. Install this package only when an
9109
+ application wants the agent product.
9110
+
9111
+ RPC failures return actionable bounded error messages by default. For a trusted
9112
+ loopback debugger, set `exposeErrorStacks: true` (or pass
9113
+ `--expose-error-stacks` to `nifra-agent --rpc`) to include bounded exception
9114
+ stacks. This option is rejected for remote binding.
9115
+
9116
+ `IsolatedExtensionWorker` is an opt-in process-backed crash-containment seam for
9117
+ extensions. It is not a hostile-code sandbox; use OS-level isolation before
9118
+ loading code you do not trust.
9119
+
9120
+ `NifraBackend` is a small provider port for a future native backend. It accepts
9121
+ an injected model implementation and bounded tools without importing a provider
9122
+ SDK. `ReplayBackend` and `readReplayEvents` provide deterministic protocol
9123
+ replays for CI and Workbench regression tests.
9124
+
9125
+ ## At-least-once run dispatch
9126
+
9127
+ `@nifrajs/coding-agent/orchestration` also exports an evidence-only run dispatch port and an adapter
9128
+ over `@nifrajs/jobs`. Dispatch is at-least-once: a lease may be delivered again after worker loss or
9129
+ lease expiry. Every logical node attempt receives a stable idempotency key derived from the plan,
9130
+ run, node, and attempt boundary. Effects must provide a matching proof before recovery can settle a
9131
+ completed boundary; an unproven post-effect failure is dead-lettered rather than guessed safe.
9132
+
9133
+ `createMemoryRunDispatchStore()` is disposable single-process test infrastructure. It makes no
9134
+ durability, hosted-worker, retention, tenant-authorization, or exactly-once guarantee. Durable
9135
+ operated adapters implement the exported `RunDispatchStore` and `DurableDispatchAdapter` ports in
9136
+ their own data layer, including authorization, row policy, retention, reconciliation, and worker
9137
+ health. The public package does not store prompts, model output, tool arguments, or job payloads in
9138
+ its dispatch evidence.
9139
+
9140
+ Legacy `FileSessionStore` files remain supported for explicit local compatibility. Use
9141
+ `nifra-agent --migrate-session <id> --migrate-from <legacy-dir> --migrate-to <evidence-dir>` to
9142
+ create a separately validated, evidence-only target. The source is left untouched and the command
9143
+ does not switch an active configuration pointer; see
9144
+ [`docs/agent-platform/protocol-and-session-migration.md`](../../docs/agent-platform/protocol-and-session-migration.md)
9145
+ for rollback and protocol compatibility details.
9146
+
9147
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
9148
+ [`../../llms-full.txt`](../../llms-full.txt).
9149
+
7999
9150
  ## @nifrajs/content
8000
9151
 
8001
9152
  Typed, schema-validated content collections for nifra - Markdown + frontmatter, framework-agnostic.
@@ -8013,6 +9164,31 @@ bun add @nifrajs/content
8013
9164
  - Reference: <https://nifra.dev/docs>
8014
9165
  - AI-readable: <https://nifra.dev/llms.txt>
8015
9166
 
9167
+ ## Typed indexes and joins
9168
+
9169
+ Build an equality lookup and deterministic query index from a baked collection. `where` is field
9170
+ equality, `range` supports typed string/number comparisons, sorting is stable (slug is the tie-breaker),
9171
+ and pages use an opaque cursor rather than an offset:
9172
+
9173
+ ```ts
9174
+ const posts = indexCollection(await bakeCollection(blog), {
9175
+ by: "section",
9176
+ sort: { field: "publishedAt", dir: "desc" },
9177
+ })
9178
+
9179
+ const page = posts.query({ where: { section: "news" }, limit: 20 })
9180
+ const next = page.nextCursor === undefined
9181
+ ? undefined
9182
+ : posts.query({ where: { section: "news" }, cursor: page.nextCursor, limit: 20 })
9183
+ ```
9184
+
9185
+ `posts.baked` is JSON-safe and can be serialized into an edge bundle; `fromBakedIndex` rehydrates it
9186
+ without filesystem access. `joinCollections(left, right, "id")` gives one-to-many rows by default.
9187
+ Pass `{ cardinality: "one-to-one" }` to require unique right-side keys; duplicate right keys fail the
9188
+ build loudly. The join key must exist on both frontmatter types with the same TypeScript value type.
9189
+ These APIs index only validated local/baked content; credentialed CMS ingestion and tenant data remain
9190
+ outside this public package.
9191
+
8016
9192
  MIT
8017
9193
 
8018
9194
  ## For AI agents
@@ -8063,12 +9239,37 @@ import { defineChannel, memoryChannelHub } from "@nifrajs/core/channel"
8063
9239
  - **Inline or contract-first.** Write routes inline (types inferred from the
8064
9240
  builder), or `defineContract(...)` + `implement(...)` for a decoupled, versionable
8065
9241
  surface - handlers lift over unchanged.
9242
+
9243
+ ### Scaling route tables
9244
+
9245
+ Fluent chains are the best fit for a small route surface. TypeScript's type-instantiation budget
9246
+ usually becomes the limiting factor around 95-100 chained routes because every call both infers its
9247
+ handler context and re-threads the growing typed registry. The runtime router is not the limit, and
9248
+ the routes remain fully typed when you compose them as short domain groups:
9249
+
9250
+ ```ts
9251
+ const listings = server()
9252
+ .get("/listings", () => ({ ok: true }))
9253
+ .get("/listings/:id", (c) => ({ id: c.params.id }))
9254
+ const agents = server().get("/agents/:id", (c) => ({ id: c.params.id }))
9255
+
9256
+ const app = server().get("/health", () => ({ ok: true })).merge(listings).merge(agents)
9257
+ ```
9258
+
9259
+ Each group's routes keep the middleware and assurance captured when that group was defined. For a
9260
+ contract-owned surface, `defineContract(...)` + `implement(...)` is the other escape hatch: the
9261
+ registry is declared as one object type, so it does not grow one fluent-instantiation level per
9262
+ route. Split groups before the compiler reports `TS2589`, rather than weakening the route types.
8066
9263
  - **Validation at the boundary.** Per-route `body`/`query` is any
8067
9264
  [Standard Schema](https://standardschema.dev) (zod/valibot/arktype, or `@nifrajs/schema`'s
8068
9265
  `t`); invalid input is rejected with a structured `422` before the handler runs.
8069
9266
  - **Lifecycle middleware.** `derive`/`decorate` extend the typed context;
8070
9267
  `onRequest`/`beforeHandle`/`afterHandle`/`onResponse`/`onError` run around handlers;
8071
9268
  `use(middleware)` applies a bundle.
9269
+ - **Portable response observation.** The header/body/raw observer methods are opt-in so ordinary
9270
+ servers stay lean: add `responseObserver()` from `@nifrajs/core/response-observer` before calling
9271
+ `onResponseHeaders`, `onResponseBody`, or `onResponseRaw`. Official middleware that uses these
9272
+ tiers installs the compatibility runtime automatically.
8072
9273
  - **Hardening built in.** `stop({ drainMs })` graceful shutdown (+ opt-in SIGTERM/
8073
9274
  SIGINT), `requestTimeoutMs` (+ `ctx.signal` and `ctx.budget`), a streaming body-size cap, and a
8074
9275
  redacting structured `Logger`.
@@ -8283,6 +9484,98 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
8283
9484
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
8284
9485
  done-gate, or `nifra mcp` to give the agent live project tools.
8285
9486
 
9487
+ ## @nifrajs/edge
9488
+
9489
+ Compact fetch-handler server for edge and serverless runtimes - Cloudflare Workers, Vercel Edge, Deno Deploy, Bun. Keeps the `server().get().post()` DX and the full request trust boundary (bounded body read, Content-Length pre-reject, prototype-pollution guard, JSON / urlencoded framing) in a fraction of the bundle. Zero runtime dependencies beyond `@nifrajs/core`.
9490
+
9491
+ 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`.
9492
+
9493
+ ## Why
9494
+
9495
+ `@nifrajs/core`'s `server()` carries lifecycle hooks, cookies, response contracts, deadlines, WebSockets, and a plugin system. On an edge worker you often want none of that - just typed routing and a safe body. `@nifrajs/edge` is that subset: the same routing DX, the *same* body trust boundary (imported from core, never re-derived, so there is no second copy of the security contract to drift), and nothing else.
9496
+
9497
+ The rejection envelopes are byte-for-byte the full Server's - so an app can start on `@nifrajs/edge` and graduate to `@nifrajs/core`'s `server()` without its clients noticing.
9498
+
9499
+ ## Install
9500
+
9501
+ ```sh
9502
+ bun add @nifrajs/edge @nifrajs/core
9503
+ ```
9504
+
9505
+ ## Use
9506
+
9507
+ ```ts
9508
+ // worker.ts
9509
+ import { server, type StandardSchemaV1 } from "@nifrajs/edge"
9510
+
9511
+ const createUser: StandardSchemaV1<{ name: string; age: number }> = /* zod, valibot, arktype, ... */
9512
+
9513
+ const app = server()
9514
+ .get("/users/:id", (c) => ({ id: c.params.id }))
9515
+ .post("/users", { body: createUser }, (c) => ({ created: c.body.name, age: c.body.age }))
9516
+
9517
+ export default app // { fetch } - a module worker handler
9518
+ ```
9519
+
9520
+ `c.params` is typed from the route pattern. `c.body` is narrowed to the schema's output. Return a value (rendered as JSON) or a `Response` for full control. Throw a `Response` for an early exit; any other throw becomes a flat `500`.
9521
+
9522
+ ## Context
9523
+
9524
+ | Member | What |
9525
+ | --- | --- |
9526
+ | `c.request` | the raw `Request` |
9527
+ | `c.params` | path params, typed from the pattern (`/users/:id` -> `{ id: string }`) |
9528
+ | `c.body` | parsed, schema-validated body (`undefined` on a body-less route) |
9529
+ | `c.query()` | query string parsed to an object, on demand |
9530
+ | `c.header(name)` | a request header (case-insensitive), or `null` |
9531
+
9532
+ ## Options
9533
+
9534
+ ```ts
9535
+ server({ maxBodyBytes: 256_000, protoPoisoning: "strip" })
9536
+ ```
9537
+
9538
+ | Option | Default | What |
9539
+ | --- | --- | --- |
9540
+ | `maxBodyBytes` | `1_000_000` | body-size cap before a `413`, matching core |
9541
+ | `protoPoisoning` | `"reject"` | `__proto__` policy for JSON bodies: `"reject"` \| `"strip"` \| `"ignore"`, matching core |
9542
+
9543
+ ## Security
9544
+
9545
+ The body trust boundary is on by default and imported from `@nifrajs/core` - the same lane the full Server runs, so there is one implementation, never a trimmed copy that could drift. On every body-bearing route it enforces:
9546
+
9547
+ | Guard | Effect |
9548
+ | --- | --- |
9549
+ | Streaming byte cap | a length-less (chunked) body is drained under `maxBodyBytes`; once over, the stream is **cancelled** and rejected `413` - it is never buffered whole |
9550
+ | Content-Length pre-reject | a declared length over `maxBodyBytes` is `413` before a byte is read; a malformed length is `400` |
9551
+ | Prototype-pollution guard | an own `__proto__` key (or a poisoning-shaped `constructor.prototype`) in a JSON body is rejected (`"reject"`, default) or removed (`"strip"`) before it reaches your handler |
9552
+ | Media-type framing | JSON and urlencoded bodies are framed and capped; other content types get `415` |
9553
+
9554
+ Rejection envelopes are byte-for-byte the full Server's.
9555
+
9556
+ **Whether you need this depends on the runtime:**
9557
+
9558
+ - **Stream runtimes** (Cloudflare Workers, Deno Deploy, Bun): the runtime hands your worker a live request stream with no size ceiling below its own memory limit. A single length-less request into an unguarded `.json()` buffers unbounded and can cross the isolate memory limit (~128 MB on Workers) - one request, one killed worker (and on auto-scaling platforms, a metered bill). Here the byte cap is **load-bearing**, not optional.
9559
+ - **Pre-capped serverless** (Vercel Edge ~4.5 MB, AWS API Gateway 6-10 MB, GCP 10-32 MB, Azure ~30 MB default): the platform already bounds the body at its edge, so the crash case is largely its job. The cap still earns its place by tightening that platform ceiling to your app's real intent and returning a consistent `413`. (Platform limits are documented ceilings - check current docs for your plan.)
9560
+
9561
+ The prototype-pollution guard is **platform-independent**: no runtime guards `JSON.parse` for you, and a `__proto__`-bearing object becomes pollution the moment downstream code merges it. The guard stops it at ingress. Both defenses tune via [Options](#options).
9562
+
9563
+ ## Not included
9564
+
9565
+ By design: no lifecycle / `around` hooks, cookies, response contracts, deadlines, WebSockets, or plugins. Reach for `@nifrajs/core`'s `server()` when an app needs those.
9566
+
9567
+ ## License
9568
+
9569
+ MIT
9570
+
9571
+ ## For AI agents
9572
+
9573
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
9574
+ one cheap read instead of the whole corpus. For the wider framework: the repo's
9575
+ [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
9576
+ [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
9577
+ done-gate, or `nifra mcp` to give the agent live project tools.
9578
+
8286
9579
  ## @nifrajs/env
8287
9580
 
8288
9581
  Typed, validated environment variables. Define a schema once at startup; get a **frozen, typed**
@@ -8377,6 +9670,126 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
8377
9670
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
8378
9671
  done-gate, or `nifra mcp` to give the agent live project tools.
8379
9672
 
9673
+ ## @nifrajs/graphql
9674
+
9675
+ 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.
9676
+
9677
+ 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.
9678
+
9679
+ ## Install
9680
+
9681
+ ```bash
9682
+ bun add @nifrajs/graphql graphql
9683
+ # subscriptions also need:
9684
+ bun add graphql-ws
9685
+ ```
9686
+
9687
+ `graphql` is a required peer; `graphql-ws` is optional (only for subscriptions).
9688
+
9689
+ ## Queries and mutations
9690
+
9691
+ ```ts
9692
+ import { server } from "@nifrajs/core"
9693
+ import { mountGraphql } from "@nifrajs/graphql"
9694
+ import { buildSchema } from "graphql"
9695
+
9696
+ const schema = buildSchema(`
9697
+ type Query { hello: String }
9698
+ type Mutation { echo(msg: String!): String }
9699
+ `)
9700
+
9701
+ const rootValue = {
9702
+ hello: () => "world",
9703
+ echo: ({ msg }: { msg: string }) => msg,
9704
+ }
9705
+
9706
+ const app = server()
9707
+ mountGraphql(app, { schema, rootValue })
9708
+ // POST /graphql - queries + mutations
9709
+ // GET /graphql - queries only (idempotent, cacheable)
9710
+ ```
9711
+
9712
+ Prefer the handler directly? Mount it yourself:
9713
+
9714
+ ```ts
9715
+ import { respondGraphql } from "@nifrajs/graphql"
9716
+
9717
+ app.post("/graphql", (c) => respondGraphql(c.req, { schema, rootValue }))
9718
+ ```
9719
+
9720
+ ## Context injection
9721
+
9722
+ 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).
9723
+
9724
+ ```ts
9725
+ mountGraphql(app, {
9726
+ schema,
9727
+ context: ({ request, nifra }) => ({
9728
+ user: nifra?.env, // nifra route context, when mounted via mountGraphql
9729
+ ip: request.headers.get("x-forwarded-for"),
9730
+ }),
9731
+ })
9732
+ ```
9733
+
9734
+ Resolvers read it as their third argument.
9735
+
9736
+ ## Subscriptions
9737
+
9738
+ 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.
9739
+
9740
+ ```ts
9741
+ import { server } from "@nifrajs/core"
9742
+ import { websocket } from "@nifrajs/core/ws"
9743
+ import { mountGraphql, createPubSub } from "@nifrajs/graphql"
9744
+
9745
+ const pubsub = createPubSub<number>()
9746
+
9747
+ const app = server().use(websocket())
9748
+ mountGraphql(app, {
9749
+ schema,
9750
+ context: () => ({ pubsub }),
9751
+ subscriptions: true,
9752
+ })
9753
+
9754
+ // A resolver:
9755
+ // count: { subscribe: (_, __, ctx) => ctx.pubsub.subscribe("count"), resolve: (n) => n }
9756
+ // Push an event from anywhere:
9757
+ pubsub.publish("count", 1)
9758
+ ```
9759
+
9760
+ The route must negotiate the `graphql-transport-ws` subprotocol at the WebSocket layer for a browser `graphql-ws` client to connect.
9761
+
9762
+ ## Options
9763
+
9764
+ | Option | Default | Purpose |
9765
+ | --- | --- | --- |
9766
+ | `schema` | - | Executable `GraphQLSchema` (required). |
9767
+ | `context` | `{}` | Build the resolver `contextValue` per operation. |
9768
+ | `rootValue` | - | Root value passed to the executor. |
9769
+ | `maxBodyBytes` | `1_000_000` | Request body cap; larger bodies answer 413. |
9770
+ | `protoPoisoning` | `"reject"` | Prototype-pollution policy for the JSON body. |
9771
+ | `allowedOrigins` | reflect `*` | CORS origin allowlist. |
9772
+ | `authorize` | - | Per-request guard; return `false` for 401. |
9773
+ | `enableGet` | `true` | Serve `GET /graphql` (queries only). |
9774
+ | `subscriptions` | `false` | `true` (reuse schema/context) or explicit WS options. |
9775
+ | `legacyJsonResponse` | `false` | Old always-200 `application/json` shape. |
9776
+
9777
+ ## Responses
9778
+
9779
+ 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.
9780
+
9781
+ ## License
9782
+
9783
+ MIT
9784
+
9785
+ ## For AI agents
9786
+
9787
+ Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
9788
+ one cheap read instead of the whole corpus. For the wider framework: the repo's
9789
+ [`AGENTS.md`](../../AGENTS.md) is the copy-paste quick reference, and
9790
+ [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
9791
+ done-gate, or `nifra mcp` to give the agent live project tools.
9792
+
8380
9793
  ## @nifrajs/i18n
8381
9794
 
8382
9795
  Framework-agnostic i18n for nifra - locale negotiation + a tiny ICU message formatter on the platform Intl. Dependency-free.
@@ -8463,6 +9876,32 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
8463
9876
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
8464
9877
  done-gate, or `nifra mcp` to give the agent live project tools.
8465
9878
 
9879
+ ## @nifrajs/island-trigger
9880
+
9881
+ Framework-neutral, browser-side trigger scheduling shared by Nifra’s two island runtimes:
9882
+ `@nifrajs/web/islands` and `@nifrajs/islets`.
9883
+
9884
+ It has no DOM or UI-framework dependency. The public contract is the small `scheduleTrigger`
9885
+ primitive with these strategies:
9886
+
9887
+ ```ts
9888
+ import { scheduleTrigger } from "@nifrajs/island-trigger"
9889
+
9890
+ const dispose = scheduleTrigger({ media: "(min-width: 768px)" }, () => {
9891
+ // Activate the island.
9892
+ })
9893
+
9894
+ dispose() // cancel before the trigger fires
9895
+ ```
9896
+
9897
+ `load`, `idle`, `visible`, and media-query triggers are one-shot. Idle and visibility strategies
9898
+ fall back safely when the browser capability is unavailable; malformed or oversized media queries
9899
+ remain inert. The package only schedules the callback - each island runtime owns its own markers,
9900
+ registry, signals, and framework integration.
9901
+
9902
+ For AI agents, see [`LLM.md`](./LLM.md). The wider reference is in
9903
+ [`llms-full.txt`](../../llms-full.txt).
9904
+
8466
9905
  ## @nifrajs/islets
8467
9906
 
8468
9907
  Fine-grained signals + declarative DOM bindings for islands - **interactivity in ~1.4 KB gz**, no
@@ -8591,6 +10030,12 @@ const q = createQueue({ store: new RedisJobStore(redis) })
8591
10030
  Leasing is at-least-once: a leased job is hidden for `leaseMs`; a worker that dies mid-job releases it
8592
10031
  back automatically. Make handlers **idempotent**.
8593
10032
 
10033
+ The queue remains agent-agnostic. `@nifrajs/coding-agent` can place content-free run-dispatch
10034
+ identities in a dedicated `JobStore`, but this package does not import or define agent concepts. A
10035
+ `MemoryJobStore` is disposable single-process storage; production durability, authorization,
10036
+ retention, reconciliation, and worker coordination belong to the caller's operated adapter. No
10037
+ exactly-once delivery guarantee is implied by a lease.
10038
+
8594
10039
  ## Cloudflare Workers
8595
10040
 
8596
10041
  Workers has no long-lived process, so don't call `start()`. Back the queue with a durable store and a
@@ -8650,6 +10095,10 @@ Mount `mcp.fetch` at `POST /mcp`. The package also exposes the JSON-RPC protocol
8650
10095
  layers directly, plus `defineMcpWidget` and the React adapter for tool results that render UI in MCP
8651
10096
  hosts.
8652
10097
 
10098
+ Clients that include `text/event-stream` in `Accept` receive progress notifications as they happen,
10099
+ followed by the final JSON-RPC response in the same stream. An SSE `GET /mcp` opens a cancellable
10100
+ server-message connection; a plain `GET /mcp` remains a health page.
10101
+
8653
10102
  ## For AI agents
8654
10103
 
8655
10104
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -8779,7 +10228,9 @@ Bun GET, byte-identical on the wire). They are defaults - anything the request p
8779
10228
  `securityHeaders` and the default `poweredBy` ship this way.
8780
10229
 
8781
10230
  A middleware whose response hook only reads/writes **headers** but needs the request should use the
8782
- portable `onResponseHeaders` hook - one implementation, fast on every runtime: it mutates the
10231
+ portable `onResponseHeaders` hook - enable the observer surface with
10232
+ `app.use(responseObserver())` from `@nifrajs/core/response-observer` before registering a custom
10233
+ hook. It remains one implementation, fast on every runtime: it mutates the
8783
10234
  response's own `Headers` on Bun/Deno and the outcome record on Node's direct socket writer, never
8784
10235
  materializing Web `Request`/`Response` objects. `cors` (origin reflection), static `cacheControl`,
8785
10236
  `language`, and `poweredBy({ respectExisting: false })` are built on it. Stateful middleware (`rateLimit` with the built-in key derivation,
@@ -8898,7 +10349,13 @@ authority. If neither option is set, treat the host in `request.url` as client-c
8898
10349
 
8899
10350
  The optional `static` root is served before `app.fetch` for GET and HEAD requests. It is a
8900
10351
  public asset directory and must contain nothing private: no secrets, source maps, build
8901
- metadata, or user uploads.
10352
+ metadata, or user uploads. The directory and its ancestor path must be read-only and owned by
10353
+ the service (or otherwise protected from untrusted local writers). Nifra resolves and confines
10354
+ the file before opening it and refuses symlinked final files, but Node does not expose a native
10355
+ per-component `openat` primitive; a process that can mutate the tree concurrently can still
10356
+ create a local TOCTOU race. Mutable or attacker-writable static trees are therefore outside the
10357
+ adapter's guarantee. Store user-controlled files outside the static root and serve them through
10358
+ an application or object-storage path with its own validation policy.
8902
10359
 
8903
10360
  ## Graceful shutdown on signals
8904
10361
 
@@ -9023,6 +10480,43 @@ one cheap read instead of the whole corpus. For the wider framework: the repo's
9023
10480
  [`llms-full.txt`](../../llms-full.txt) is the full machine-readable corpus. Run `nifra check` as the
9024
10481
  done-gate, or `nifra mcp` to give the agent live project tools.
9025
10482
 
10483
+ ## @nifrajs/pi
10484
+
10485
+ Lightweight Pi JSONL/RPC adapter for the Nifra agent protocol.
10486
+
10487
+ It starts Pi in headless RPC mode, maps Pi lifecycle, assistant, tool,
10488
+ compaction, extension, and error events into `@nifrajs/agent-protocol`, and
10489
+ supports cancellation, reload, session identity, and bounded event buffering.
10490
+
10491
+ Reload defaults to a process restart with the same Pi session ID. This is the
10492
+ reliable reload primitive in Pi's documented RPC protocol: the transcript stays
10493
+ in Pi's session store while the new process loads changed extensions. The tiny
10494
+ public-API bridge is still loaded for interactive Pi use. Set
10495
+ `reloadCommand: "prompt"` only for an RPC implementation that dispatches slash
10496
+ commands, or `reloadCommand: "rpc"` for one that provides a top-level reload
10497
+ command. Set `enableReloadBridge: false` when supplying a custom bridge.
10498
+
10499
+ Set `enableNifraTools: true` to load the separately packaged Pi extension with
10500
+ `nifra_context`, `nifra_check`, `nifra_assure`, and `nifra_test` tools. The
10501
+ default is off so embedding Pi stays minimal.
10502
+
10503
+ ```ts
10504
+ import { PiBackend } from "@nifrajs/pi"
10505
+
10506
+ const backend = new PiBackend()
10507
+ const session = await backend.createSession({ cwd: process.cwd() })
10508
+ for await (const event of backend.send({ sessionId: session.id, message: "inspect this project" })) {
10509
+ console.log(event.type)
10510
+ }
10511
+ ```
10512
+
10513
+ Pi stays an optional dependency of the agent product. Nothing in this package is
10514
+ reachable from `@nifrajs/core`, `@nifrajs/client`, `@nifrajs/web`, or
10515
+ `@nifrajs/schema`.
10516
+
10517
+ For AI agents, see [`LLM.md`](./LLM.md) and the full corpus
10518
+ [`../../llms-full.txt`](../../llms-full.txt).
10519
+
9026
10520
  ## @nifrajs/prompt
9027
10521
 
9028
10522
  Provider-neutral, type-safe prompts with Standard Schema input and validated structured output.
@@ -9076,7 +10570,7 @@ const upstream = createProxy({ upstream: "http://127.0.0.1:8081" })
9076
10570
  const app = server().mountFetch("/api", upstream, { stripPrefix: true })
9077
10571
  ```
9078
10572
 
9079
- 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`.
10573
+ 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`.
9080
10574
 
9081
10575
  ## Docs
9082
10576
 
@@ -9238,6 +10732,11 @@ pi install npm:@nifrajs/skills
9238
10732
  /plugin install nifra@nifra
9239
10733
  ```
9240
10734
 
10735
+ The plugin registers the Nifra MCP server as well as the skills, so a fresh install has the live tools
10736
+ the skills tell it to prefer. Its launch spec is deliberately unpinned - a plugin is installed once and
10737
+ opened against many projects, so the CLI resolves per project and the server reports version skew
10738
+ itself rather than freezing one version across all of them.
10739
+
9241
10740
  **Anything that reads a skills directory** (Cursor, `.agents/skills`, a project `.pi/skills`)
9242
10741
 
9243
10742
  ```sh
@@ -9254,6 +10753,9 @@ against the version installed in the project being edited:
9254
10753
  bunx @nifrajs/cli init-agents # register the MCP server in an existing app
9255
10754
  ```
9256
10755
 
10756
+ The Claude Code plugin brings its own copy of that server, so `init-agents` is for the other surfaces -
10757
+ and for committing a version-pinned `.mcp.json` that every contributor on the project shares.
10758
+
9257
10759
  Not in a Nifra project? The docs tools are hosted at `https://mcp.nifra.dev`.
9258
10760
 
9259
10761
  That split is the anti-drift design: a skill that restated the API would rot on the next release,
@@ -9533,6 +11035,14 @@ that every call carries and captures cookies via a shared jar.
9533
11035
 
9534
11036
  For a **stateless** request (no cookies), use `testClient` from `@nifrajs/client` directly.
9535
11037
 
11038
+ ### Agent evaluation and failure matrix
11039
+
11040
+ `runAgentEvalComposition` composes trajectory replay, fault profiles, contract invariants,
11041
+ idempotency proofs, and adapter certification. Reports expose explicit invariant and rubric cases,
11042
+ stable regression IDs, schedule tokens, and digests; they do not retain prompts, tool payloads,
11043
+ model output, labels, or a single opaque quality score. The failure matrix covers model, tool,
11044
+ approval, cancellation, lease, cursor, registry, and deployment boundaries.
11045
+
9536
11046
  ## For AI agents
9537
11047
 
9538
11048
  Start with [`LLM.md`](./LLM.md) - this package's contract card (the exports you call + its footguns),
@@ -9743,6 +11253,14 @@ Zero-framework render adapter for @nifrajs/web - auto-escaping tagged-template H
9743
11253
 
9744
11254
  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`.
9745
11255
 
11256
+ ## The AI-safe lane
11257
+
11258
+ 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.
11259
+
11260
+ 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.
11261
+
11262
+ 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.
11263
+
9746
11264
  ## Install
9747
11265
 
9748
11266
  ```sh
@@ -9903,13 +11421,29 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9903
11421
 
9904
11422
  # Complete export index (generated from each package's src/index.ts)
9905
11423
 
11424
+ ## @nifrajs/a2a
11425
+
11426
+ `A2A_ERROR_CODES`, `A2A_PROTOCOL_VERSION`, `A2AAgentCard`, `A2AAgentSkill`, `A2ACardInfo`, `A2AMountableApp`, `A2ARouteContext`, `A2ATaskState`, `AGENT_CARD_WELL_KNOWN_PATH`, `agentCard`, `mountA2A`, `MountA2AOptions`
11427
+
11428
+ ## @nifrajs/ag-ui
11429
+
11430
+ `AgUIMountableApp`, `AgUIRouteContext`, `AgUIRunContext`, `AgUIRunUsage`, `mountAgUI`, `MountAgUIOptions`
11431
+
9906
11432
  ## @nifrajs/agent
9907
11433
 
9908
- `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`
11434
+ `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`
11435
+
11436
+ ## @nifrajs/agent-app
11437
+
11438
+ `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`
11439
+
11440
+ ## @nifrajs/agent-protocol
11441
+
11442
+ `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`
9909
11443
 
9910
11444
  ## @nifrajs/agent-telemetry
9911
11445
 
9912
- `agentTelemetry`, `AgentTelemetryOptions`, `consoleAgentExporter`
11446
+ `AgentRunEvidence`, `AgentRunOutcome`, `AgentRunTrace`, `agentTelemetry`, `AgentTelemetryOptions`, `consoleAgentExporter`, `orchestrationTelemetry`, `OrchestrationTelemetry`, `OrchestrationTelemetryEvent`, `OrchestrationTelemetryKind`, `OrchestrationTelemetryOptions`, `traceAgentRun`, `TraceAgentRunOptions`
9913
11447
 
9914
11448
  ## @nifrajs/auth
9915
11449
 
@@ -9929,15 +11463,19 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9929
11463
 
9930
11464
  ## @nifrajs/client
9931
11465
 
9932
- `ActionArgs`, `ActionData`, `ApiError`, `ApiProxy`, `client`, `ClientOptions`, `ClientRetryOptions`, `FetchFn`, `inProcessClient`, `InProcessClient`, `InProcessClientOptions`, `Jsonify`, `LoaderArgs`, `LoaderData`, `RegistryOf`, `ResponseContractViolation`, `Result`, `SubscribeOptions`, `Subscription`, `testClient`, `Treaty`, `TreatyFromRegistry`, `WsCallOptions`, `WsHandle`
11466
+ `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`
11467
+
11468
+ ## @nifrajs/coding-agent
11469
+
11470
+ `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`
9933
11471
 
9934
11472
  ## @nifrajs/content
9935
11473
 
9936
- `bakeCollection`, `BakedCollection`, `Entry`, `fromBaked`, `InferSchema`, `parseEntry`, `ParseEntryOptions`, `parseFrontmatter`, `StandardSchemaV1`, `StaticCollection`
11474
+ `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`
9937
11475
 
9938
11476
  ## @nifrajs/core
9939
11477
 
9940
- `Version`, `VERSION`
11478
+ `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`
9941
11479
 
9942
11480
  ## @nifrajs/cron
9943
11481
 
@@ -9951,6 +11489,10 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9951
11489
 
9952
11490
  `devtools`, `DevToolsClientOptions`, `devtoolsClientScript`, `DevToolsEvent`, `DevToolsOptions`, `DevToolsQuery`, `filterDevToolsEvents`
9953
11491
 
11492
+ ## @nifrajs/edge
11493
+
11494
+ `EdgeContext`, `EdgeHandler`, `EdgeOptions`, `EdgeServer`, `Method`, `Params`, `QueryValue`, `server`, `StandardSchemaV1`, `toFetchHandler`
11495
+
9954
11496
  ## @nifrajs/env
9955
11497
 
9956
11498
  `boolean`, `defineEnv`, `DefineEnvOptions`, `enumValue`, `env`, `EnvResult`, `EnvShape`, `InferOutput`, `number`, `port`, `StandardResult`, `StandardSchemaV1`, `string`, `url`
@@ -9959,6 +11501,10 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9959
11501
 
9960
11502
  `createEventRegistry`, `defineEventContract`, `EventContract`, `EventContractError`, `EventEnvelope`, `EventParseResult`, `EventRegistry`, `RegistryParseResult`
9961
11503
 
11504
+ ## @nifrajs/graphql
11505
+
11506
+ `buildContext`, `createPubSub`, `GraphqlContextBuilder`, `GraphqlContextInput`, `GraphqlHttpOptions`, `GraphqlPubSub`, `graphqlWebSocket`, `GraphqlWsOptions`, `mountGraphql`, `MountGraphqlOptions`, `NifraContextLike`, `respondGraphql`
11507
+
9962
11508
  ## @nifrajs/i18n
9963
11509
 
9964
11510
  `createFormatter`, `Formatter`, `Locale`, `LocaleParts`, `LocaleSource`, `Messages`, `negotiateLocale`, `NegotiateOptions`, `ResolvedLocale`, `resolveLocale`
@@ -9967,9 +11513,13 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9967
11513
 
9968
11514
  `cloudflareLoader`, `CloudflareLoaderOptions`, `createPngRasterizer`, `createResvgRasterizer`, `HtmlImageAttrs`, `identityLoader`, `imageDimensions`, `ImageFormat`, `ImageInfo`, `ImageLoader`, `ImageProps`, `OgImageOptions`, `OgImageRasterized`, `OgImageRasterizer`, `ogImageResponse`, `PngRenderer`, `readImageDimensions`, `renderOgImage`, `ResolvedImage`, `resolveImage`, `ResvgConstructor`, `ResvgRenderer`, `selfHostedLoader`, `SelfHostedLoaderOptions`, `signImageUrl`, `SignImageUrlOptions`, `toHtmlAttrs`
9969
11515
 
11516
+ ## @nifrajs/island-trigger
11517
+
11518
+ `IslandStrategy`, `MAX_MEDIA_QUERY_LENGTH`, `scheduleTrigger`, `TriggerOptions`
11519
+
9970
11520
  ## @nifrajs/islets
9971
11521
 
9972
- `batch`, `BindableElement`, `BindableRoot`, `bindScope`, `computed`, `effect`, `island`, `IslandContext`, `IslandHost`, `IslandScope`, `IslandSetup`, `islandState`, `mountIslands`, `signal`, `Signal`
11522
+ `batch`, `BindableElement`, `BindableRoot`, `bindScope`, `computed`, `effect`, `island`, `IslandContext`, `IslandHost`, `IslandScope`, `IslandSetup`, `islandState`, `IslandStrategy`, `MountIslandOptions`, `mountIslands`, `signal`, `Signal`
9973
11523
 
9974
11524
  ## @nifrajs/jobs
9975
11525
 
@@ -9999,13 +11549,17 @@ done-gate, or `nifra mcp` to give the agent live project tools.
9999
11549
 
10000
11550
  `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`
10001
11551
 
11552
+ ## @nifrajs/pi
11553
+
11554
+ `PiBackend`, `PiBackendOptions`
11555
+
10002
11556
  ## @nifrajs/prompt
10003
11557
 
10004
11558
  `prompt`, `Prompt`, `PromptInputError`, `PromptMessage`, `PromptOutputError`, `PromptRequest`, `PromptResponseFormat`, `RunOptions`
10005
11559
 
10006
11560
  ## @nifrajs/proxy
10007
11561
 
10008
- `createProxy`, `ProxyContext`, `ProxyHandler`, `ProxyOptions`
11562
+ `createProxy`, `fetchTransport`, `FetchTransportOptions`, `NativeProxyTransport`, `NativeProxyTransportRequest`, `NativeProxyUpstreamResponse`, `NodeNativeProxyRequest`, `NodeNativeProxyResponse`, `ProxyContext`, `ProxyHandler`, `ProxyOptions`, `ProxyTransport`, `ProxyUpstreamRequest`, `ProxyUpstreamResponse`
10009
11563
 
10010
11564
  ## @nifrajs/runner
10011
11565
 
@@ -10021,7 +11575,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10021
11575
 
10022
11576
  ## @nifrajs/testing
10023
11577
 
10024
- `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`
11578
+ `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`
10025
11579
 
10026
11580
  ## @nifrajs/ts-plugin
10027
11581
 
@@ -10033,7 +11587,7 @@ done-gate, or `nifra mcp` to give the agent live project tools.
10033
11587
 
10034
11588
  ## @nifrajs/web
10035
11589
 
10036
- `Action`, `ACTION_GLOBAL`, `apiKey`, `assertRenderAdapterConformance`, `Blocker`, `BlockerController`, `BlockerFunction`, `BlockerLocation`, `BlockerState`, `BrowserNavigate`, `buildManifest`, `CachedResponse`, `CacheStore`, `canonical`, `clientEntry`, `ClientRouter`, `ClientRouterOptions`, `createClientRouter`, `createMatcher`, `createMutation`, `createQueryClient`, `createWebApp`, `CreateWebAppOptions`, `DATA_GLOBAL`, `DATA_HEADER`, `DEFAULT_DEV_PORT`, `defer`, `Deferred`, `DehydratedState`, `disableDraft`, `DRAFT_COOKIE`, `DraftCookieControls`, `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`, `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`, `mergeHeads`, `meta`, `Meta`, `MetaArgs`, `MetaDescriptor`, `MetaInput`, `MountRouterOptions`, `MutationCallbacks`, `MutationHandle`, `MutationState`, `MutationStatus`, `NAV_FROM_HEADER`, `NavigateFunction`, `NavigateOptions`, `NavigateSearchOf`, `NavigateTargetInput`, `notFound`, `openGraph`, `OpenGraphInput`, `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`, `resolveMeta`, `resolveNavigate`, `resolvePublicPath`, `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`, `StaticPath`, `StaticPaths`, `StaticRoutes`, `STATUS_HEADER`, `statusPage`, `StatusPageOptions`, `styles`, `Submission`, `SubmitOptions`, `unsafeInlineScript`, `UnsafeScriptDescriptor`, `withISR`
11590
+ `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`
10037
11591
 
10038
11592
  ## @nifrajs/web-preact
10039
11593