<!-- Generated by `pnpm changelog:generate` from the "## Release notes" sections of merged pull requests. Do not edit this file by hand: change the PR's release notes and regenerate. -->

# @kya-os/checkpoint-express

Fail-closed defaults. Breaking for hosts that relied on the old fall-through.

## 1.11.0 - 2026-10-21

### Added

- `withCheckpoint` claims an accepted signed request once, before it reaches the handler, using the dashboard's shared ledger when `apiKey` and `projectId` are set, or an in-process store otherwise; the new `requestClaimStore` option picks the store. (#6422)

### Fixed

- An engine that fails to load (a missing or unreadable `.wasm`) is now logged with its cause every time, not only with `debug: true`. Requests are still answered 503, and the next request tries the load again. (#6436)

### Other merged changes

- chore(release): set nextjs and express to 1.10.0 in package.json and the version literal (#5192)
- Revert "fix(detection): classify zero-evidence requests as unknown instead of human" (#5881)
- fix(detection): classify zero-evidence requests as unknown instead of human (#5886)
- chore(deps): name the express-rate-limit version the override already installs (#6075)
- refactor(express): remove session tracking and storage adapters (#6320)
- refactor(wasm-runtime): add make engine defaults and share it across the sdks (#6303)
- perf(sdk): load lean checkpoint-shared entries in express and next.js (#6306)
- fix(wasm-runtime): stop fetching a tenant policy that does not exist (#6301)
- fix(sdk): raise the node floor to the versions that can require an es module (#6293)
- refactor(wasm-runtime): one composed-policy context for express and next.js (#6360)

### Notes (#5387)

- Browser posture is also read from the `KYA-Posture` and
  `KYA-Posture-Session` request headers, which a page on another host, or one
  that blocks cookies, sends from the beacon's `onReceipt`. Each header wins
  over its cookie. Needs `@kya-os/checkpoint-wasm-runtime` and
  `@kya-os/checkpoint-shared` releases that carry `postureFromRequest`.

### Notes (#5594)

- `getCheckpointPostureOutcome(req)` lets a route handler read the browser posture
  outcome the middleware already computed, `{ status, reason?, posture? }`, instead
  of verifying the token again. `undefined` means posture is not configured or the
  middleware did not run, which is unknown and not the same as `{ status: 'absent' }`.
  Needs a `@kya-os/checkpoint-wasm-runtime` release that carries `postureHandlerView`.

### Notes (#5707)

- A composed-policy load timeout now preserves the structured decision, as
  documented for fail-open policy evaluation. Delegation-root loads still
  surface unavailable policy errors so a request with a delegation proof is
  not verified without the configured roots.

### zero-evidence requests are `IncompleteData`, not `Human` (breaking wire change) (#5060)

- **Behavior change:** the embedded engine no longer classifies a request that carries no evidence as `Human`. For
  that request `detectionDetail.detectionClass.type` is now `IncompleteData` and `detectionDetail.riskLevel` is
  `medium` instead of `low`. `isAgent` stays false.
- The engine marks that verdict with `detectionDetail.metadata.detection_basis = "no_evidence"`. It is the only thing
  that separates a zero-evidence request from other `IncompleteData` verdicts, and the composed-policy projection reads
  it to give the visitor the category `no_evidence`.
- A request with suspicious headers is still `IncompleteData` (the engine's internal Unknown class), but its
  `detectionDetail.verificationMethod` is now `none` instead of `pattern`.
- Composed rules aimed at all agents now exempt only `human` and `no_evidence` visitors, so any other `incomplete_data`
  visitor is matched by them. A forged `mcp_i_handshake` or `kyaos_handshake` envelope that the engine blocks projects
  as category `unknown`, not `incomplete_data`.
- For a zero-evidence request `detectionDetail.isBot` and `detectionDetail.isAiCrawler` are now absent; they were `false`.
  Read a missing value as false. Its `reasons` now read `Request did not carry enough signal to classify confidently`
  where they read `No known agent indicators matched`.
- A deployed rule that does not compare the category to `no_evidence` reads it as `human` because the composed-policy
  compile rewrites the policy text (`withNoEvidenceExclusion` in `@kya-os/checkpoint-shared`). The dashboard serves the
  stored text, so a release that predates the rewrite reads it, `@scopes` included, exactly as before.
- Composed-policy evaluation now applies that same rewrite to the policy text inside the compile, once per distinct policy
  text on a cache miss and not on every request, so a custom `PolicyFetcher` is covered and text that already carries
  the rewrite is unchanged. Apart from computed reads, a statement is skipped only when it compares the category to the literal `"no_evidence"`.
  The rewrite takes a direct read of the category, including a parenthesized principal and a bracket key written with
  escapes; whitespace between tokens is the 25 Unicode `White_Space` characters and a `//` comment ends at a line feed
  or a lone carriage return. A statement that reads the category through a computed value holding the principal, such as an `if` or a record
  that carries `principal`, is left as written and sees `no_evidence`, and a `principal` that is an attribute name or
  record key is never rewritten. If the engine rejects the rewritten text as invalid Cedar, the authored text is
  compiled instead, a `no_evidence` visitor is still read as `human` for that policy, and the new optional
  `ComposedPolicyLogger.compileFallback(projectId, error)` is called once per policy text, or `console.warn` when the
  logger does not supply it. A policy nested deeper than 32 levels is refused as invalid Cedar before the engine
  compiles it. Needs a `@kya-os/checkpoint-wasm-runtime` release that carries the rewrite.
- `engineInfo.version` and `engineInfo.rulesetVersion` are now `0.2.1` (they were `0.2.0`). `engineInfo.rulesetHash` is
  unchanged: it hashes the bundled vendor keys, vendor feeds and user-agent pattern table, and none of them changed.
- The middleware reports through `@kya-os/checkpoint-wasm-runtime`, whose reporter now sends `detection.detectionBasis`
  to `POST /api/v1/log-detection`. Needs a `@kya-os/checkpoint-wasm-runtime` release that carries it; this package needs
  no code change.
- Express has no fallback path that reports `Human`, so `Human` no longer appears in its detection results.

### Notes (#5968)

- `resolveDelegationChallenge` is now also called for a `Block` the engine marks `consent_remediable` (a scope
  shortfall against the host's required scopes, or a delegation whose own `exp` passed or whose status list says
  revoked). Return context with the route's required scopes, from your route configuration and never from
  `result.decision.reason.requested`, and the request gets the same Delegation challenge a first-contact `Challenge`
  gets, so the agent can send the human back to consent. Return `undefined` and the request keeps its block. Every
  other block still renders as before and never calls the resolver. Needs the `@kya-os/checkpoint-wasm-runtime`
  release that carries the re-challenge.

### Notes (#5652)

#### Changed

- **Breaking:** an engine or pipeline error answers `503` with `Retry-After: 3`,
  `Cache-Control: no-store` and the fixed
  `{"error":"Service temporarily unavailable","source":"checkpoint-middleware"}`
  body. It no longer calls `next(err)`, so an app error handler is not reached
  for a middleware fault (once headers are sent, the fault still goes to
  `next(err)`).
- **Breaking:** a project policy that cannot be fetched with no last-good copy
  throws `PolicyUnavailableError` and answers `503`. The default policy is
  never substituted. With a last-good copy (at most 24h old) that copy is
  served. A policy-origin 401/403/404, or a 200 body that lacks the policy
  API's `blockThreshold`, counts as an outage.
- Trusted delegation roots are resolved for every request that presents a KYA
  credential: a kya-http signature, a delegation-proof header, or an
  `Authorization: Bearer` VC-JWT. A lone Bearer VC was previously verified
  against an empty root set and accepted; with the rebuilt engine below it is
  rejected with `kyaos/chain-broken` unless the project configures trusted
  roots.
- An empty trusted-root list rejects every delegation (`kyaos/chain-broken`)
  instead of trusting any issuer; this comes with the rebuilt engine in
  `@kya-os/checkpoint-wasm-runtime`.

#### Added

- `requestPresentedCredential` from `@kya-os/checkpoint-shared` decides whether
  a request presented a KYA credential.

### Node 20.16+ or 22.3+ required; did:web and status-list fetches are hardened (breaking) (#6094)

- **BREAKING:** `engines.node` is now `^20.16.0 || >=22.3.0` (was `>=18.0.0`), so installing on older Node warns.
  Node 18 and Node 20 before 20.16 are end of life and have no `process.getBuiltinModule`, which the default DID
  resolver needs for its connect-time address check. There every did:web and status-list fetch is refused: a request
  with a did:web identity is blocked and one whose credential has a status list gets a 503. One warning per process
  names the Node version.
- **BREAKING for hosts whose only route out is a proxy:** on Node the default `didResolver` and `statusListCache`
  fetch with `https.get` and `agent: false`. That ignores an undici global dispatcher
  (`setGlobalDispatcher(new ProxyAgent(...))`) and Node 24's `NODE_USE_ENV_PROXY`, which global `fetch` honoured
  before, so such a host fails every did:web and status-list fetch (503). Workaround: pass your proxied fetch,
  `adapters: { didResolver: makeDidResolver({ fetch: proxiedFetch }), statusListCache: makeStatusListCache({ fetch: proxiedFetch }) }`
  (both from `@kya-os/checkpoint-wasm-runtime/adapters`). Redirects, ports and size stay checked, but the
  connect-time address check is then your job.
- The default DID resolver and status-list cache now refuse redirects, ports other than 443, bodies over 64 KiB and,
  on Node, hosts that resolve to a private, loopback, link-local, CGNAT or metadata address. See the
  `@kya-os/checkpoint-wasm-runtime` changelog (#3331).

### Notes (#6187)

- **Breaking:** verification and reporting now use authenticated client-address resolution instead of `req.ip`. The default `direct` policy withholds a socket that Express regards as a proxy, rather than reporting the load balancer as the visitor. Proxy deployments must select an explicit `trusted-proxy` or `cloudflare` profile with a peer allowlist. Blanket `trust proxy: true` and numeric hop settings cannot authenticate forwarding headers. Invalid, duplicate, or inexact forwarding chains return no address. See [migration steps](https://github.com/Know-That-Ai/checkpoint/blob/main/docs/runbooks/client-ip-resolution.md#migrate-existing-configurations).

### Notes (#5783)

- The browser posture reader now applies the token's `detection` claim, `automation_disclosed` and an `agent-driven` verdict, in that order, over a human or incomplete-data baseline, and `automation_corroborated` last as incomplete data. A webdriver-disclosed session now upgrades to a bot, so a policy that blocks bots now blocks it. Needs the `@kya-os/checkpoint-wasm-runtime` release that carries this reader.

### Notes (#5748)

- OpenAI Tier-1 signing keys now rotate without an SDK release. Verification
  fetches a signed key feed from `feeds.kya.vouched.id` through
  `@kya-os/checkpoint-wasm-runtime`, and a request that misses on an unknown or
  expired OpenAI key is answered at once while the feed refreshes in the background, so the next
  request uses the new keys. This adds
  outbound HTTPS from your server to that host, so allow-list it if you filter
  egress. Point it elsewhere with `KYA_VENDOR_KEY_FEED_URL`. Needs a
  `@kya-os/checkpoint-wasm-runtime` release that carries the feed hook; this
  package needs no code change.

## 1.10.0

Picks up the Zod 4 flip through its dependency ranges. No source behaviour
change in this package.

### Changed

- Dependency ranges move to the Zod 4 family: `@kya-os/checkpoint-shared`
  `^1.3.0` to `^1.4.0` and `@kya-os/checkpoint-wasm-runtime` `^1.8.2` to
  `^1.9.0`. See `@kya-os/checkpoint-shared` 1.4.0 for the validation and
  error-shape changes that come with it. This package's own public surface
  re-exports no Zod object, so nothing Zod-shaped reaches a consumer's
  `.d.ts` from here.
- Built with `tsdown` instead of `tsup` (#5131). Entrypoints, export
  conditions, and emitted formats are unchanged; this is a toolchain swap,
  but it does regenerate every file in `dist`.

### Documentation

- `@default` annotations added across the options in `src/types.ts`. No
  runtime change.

## 1.9.0: browser posture, client-IP policy, and the load fix

- Express request translation and detection reporting now share one bounded IP
  resolver (#4794).
- Added explicit direct and trusted-proxy policies. Raw XFF no longer bypasses
  the host's configured trust boundary (#4794).
- Browser posture verification (#4940): new `browserPosture` option
  `{ origin, keyId, publicKey, protectedRoutes }` (plus optional
  `sessionCookieName`); `protectedRoutes` is an array of
  `{ path, scopes }`. Verified ES256 posture is projected into Cedar as
  `context.integrity`; the default rule challenges only routes listed in
  `protectedRoutes`. Configuring `protectedRoutes` without
  `resolveDelegationChallenge` throws at startup.
- Note: 1.7.x and 1.8.0 were never published to npm. Upgrading from 1.6.0 also
  picks up #4561, which fixed the package failing to load in both CJS and ESM.

## 1.8.0 — `req.checkpoint` session-tracking alias

**Minor — additive.** `withSessionTracking` now attaches `req.checkpoint`
alongside `req.agentShield`, both pointing at the same object reference —
a mutation read through one name is visible through the other. The
`AgentShieldRequest` type augmentation (`./types`) declares both
properties; `agentShield` is marked `@deprecated` in favor of `checkpoint`
but is not removed — removing it would be a **major**. No behaviour
change for existing `req.agentShield` consumers.

## 1.7.1 — reporter now sends an absolute url when available

**Patch — additive, no engine/verification behaviour change.** The detection
reporter's `url` context field is now the absolute URL (scheme + Host header +
path + query) when the request carries a `Host` header, instead of always the
bare path. Fixes KTA site-visit reports losing hostname attribution
("using hostname \"unknown\"") for traffic behind this SDK. The `path` field is
unchanged, and the orchestrator's own request input (`expressRequestToHttpLike`
in `translate.ts`) is untouched — it must stay path-only, since it feeds the
engine's RFC 9421 `@path` signature-derived component. `req.protocol` (which
honors Express `trust proxy`) selects the scheme; no forwarded-proto parsing
was added.

## 1.7.0 — fetcher-200 envelope active by default (#3530)

**Minor — behaviour change.** The body-readable HTTP 200 challenge envelope
(draft-kya-http-02 §8.1, fetcher-200) is now the **default**: `delegationChallengeMode`
defaults to `'negotiated'` (was `'spec-401'`). A detected interactive `ai_agent`
(or a non-empty `KYA-Accept-Challenge` request header) now receives the
body-readable **200** so fetchers that drop 4xx bodies (e.g. Claude-User WebFetch)
can read the `consent_uri`; capable / discovery clients still get the spec **401**.
The body is byte-identical across both — selection is a cooperative-UX bridge,
**NOT** an access control. Set `delegationChallengeMode: 'spec-401'` to opt out.
Brings the SDK in line with the gateway worker (#3431); fleet rollout #3530.

## 1.5.2 — Self-report SDK identity (version-gating)

**Patch — additive.** `withCheckpoint`'s detection reporter now self-reports
`sdk: { name: '@kya-os/checkpoint-express', version, runtime: 'node' }` so the
dashboard can version-gate "composed policy enforces here" on the actual
installed SDK version (the @Policy version-gating work, #3076). `VERSION` is now
single-sourced (re-exported from `create-middleware`) so it can't drift from the
reporter's self-report; a unit test pins it to `package.json`. No API/behaviour
change to the middleware itself.

## 1.5.1 — Composed-policy: migrate onto the shared core

**Patch — internal refactor, no API or behaviour change.** The composed-policy
enforcer now consumes the shared core
(`@kya-os/checkpoint-wasm-runtime/composed-policy`) instead of a local copy: the
inline compile-once cache, the `verifyResultToAuthorizeInput` projection, the
empty-Cedar guard, and the act-gate were replaced by the core's
`makeComposedPolicyCache` + `verifyResultToAuthorizeInput` + `evaluateComposedPolicy`.
This collapses the last SDK-side duplicate so the DNS Gateway, Express, and
Next.js are genuinely one SSOT (the @Policy track's extract-then-migrate plan,
#3076). Public exports (`makeComposedPolicyEnforcer`,
`verifyResultToAuthorizeInput`, the types, `consoleComposedPolicyLogger`) and
behaviour (shadow-first, fail-open, empty-Cedar guard, act-gate) are unchanged.

## 1.5.0 — Composed-policy (kya-os-engine) enforcement

**Minor release — additive.** Makes a customer's `/policy-compose` output
enforce IN-PROCESS via the kya-os-engine policy seam, byte-for-byte the same
as the DNS Gateway (engine = the detection + enforcement SSOT). Without the
new `projectId` config the middleware behaves exactly as 1.4.0 (detection +
structured policy only).

### Added

- **`projectId`** on `CheckpointConfig` — opt into composed-policy
  enforcement. The project's policy is fetched from
  `${baseUrl}/api/internal/policies/${projectId}`; when it carries a deployed
  Cedar bundle with `engineEnforcementEnabled` on, the engine decision is
  enforced in-process.
  - **Shadow-first**: with a deployed bundle but enforcement off, the engine
    decision is computed and divergence from the structured decision is logged
    (gated on `debug`), but does NOT act. Flip the dashboard flag to enforce.
  - **Fail-open**: any compile/evaluation fault falls through to the
    structured decision — a policy fault never 5xxs.
  - **Empty-Cedar guard**: blank source never reaches the evaluator (empty
    Cedar compiles to deny-all).
- **`composedPolicyEnforcer`** on `CheckpointConfig` — advanced/testing seam
  to inject a pre-built enforcer (mirrors the `adapters` injection pattern).
- Public exports: `makeComposedPolicyEnforcer`, `verifyResultToAuthorizeInput`,
  and their types, for advanced composition.

### Cross-runtime parity

`verifyResultToAuthorizeInput` mirrors the DNS Gateway worker's
`buildAuthorizeInput` + `mapToGatewayDetail` semantics (category mapping,
verified-delegation allowlist, the `mcp_i_handshake` optimistic-detail
override) so the same Cedar bundle yields the same decision at the edge and
in-process.

## 1.4.0 — 2026-05-26 — Detection reporter (closes silent-onboarding bug)

**Minor release — fixes a P0 onboarding bug.** Customers who installed
`withCheckpoint` and deployed their server saw the dashboard's
"Verify connection" check fail forever with no detections received,
even though the engine was correctly classifying requests in-process.
Root cause: the engine-backed middleware ran verification via WASM
and applied verdicts to the response, but never POSTed the
`VerifyResult` back to `/api/v1/log-detection`. Dashboard's
`detections` table never saw a row, so verify-pixel returned the
generic "no middleware detections received" failure.

### Added

- **`apiKey`** on `CheckpointConfig` — wires the detection reporter
  from `@kya-os/checkpoint-wasm-runtime/reporter`. Resolve from
  `process.env.CHECKPOINT_API_KEY` in your host app. Without it, the
  engine still runs locally but no detections reach the dashboard
  (the documented opt-out for self-hosted observability stacks).
- **`baseUrl`** — override the dashboard host. Defaults to
  `https://kya.vouched.id`.
- **`debug`** — surface reporter HTTP failures via `console.warn`.
  Defaults to `false` (fire-and-forget is silent by design).

### Migration

Existing 1.3.0 deployments keep working but stay invisible to the
dashboard. To restore dashboard reporting:

```ts
app.use(
  withCheckpoint({
    tenantHost: 'your.tenant.example',
    apiKey: process.env.CHECKPOINT_API_KEY!, // <— add this
  })
);
```

Internally implemented via `@kya-os/checkpoint-wasm-runtime@1.6.0`'s
new `./reporter` subpath; both express and nextjs `withCheckpoint`
share the same transport + payload shape.

## 1.3.0 — 2026-05-18

**Minor release** — Phase-E.7 orphan throw-stub deletion. Express
sibling of Phase-D.7 (Next.js). Closes the EPIC #2573 DoD line
("`pnpm why @kya-os/agentshield-*` returns empty workspace-wide")
for the Express layer.

### Removed

- **Throw-stub files deleted entirely:** `middleware.ts`,
  `enhanced-middleware.ts`, `policy.ts`. The migration window
  introduced in 1.0.0 has elapsed; zero workspace consumers of any
  throw-stub function were found via grep, and `@kya-os/checkpoint-express`
  is now imported from exactly one TS file workspace-wide (which
  imports only `withCheckpoint`).
- **Symbols no longer exported:** `agentShield`,
  `createAgentShieldMiddleware`, `createEnhancedAgentShieldMiddleware`,
  `applyPolicy`, `evaluatePolicyForDetection`,
  `createContextFromDetection`, `handlePolicyDecision`, `getPolicy`,
  `sendBlockedResponse`, `sendRedirectResponse`,
  `sendChallengeResponse`, `EnhancedMiddlewareConfig`,
  `PolicyMiddlewareConfig`, `ExpressMiddlewareConfig`,
  `AgentShieldMiddleware`, plus the `checkpoint-shared` re-exports
  that lived in `policy.ts` (`evaluatePolicy`, `createEvaluationContext`,
  `ENFORCEMENT_ACTIONS`, `DEFAULT_POLICY`, `PolicyConfig`,
  `PolicyEvaluationContext`, `PolicyEvaluationResult`). Import the
  shared re-exports directly from `@kya-os/checkpoint-shared` if
  needed.
- **`./policy` subpath export** removed from `package.json`. The file
  it pointed at no longer exists.
- **`@kya-os/checkpoint` dependency** dropped. Audit confirmed zero
  imports from the package in this src tree.

### Preserved

- `withCheckpoint`, `CheckpointConfig` — the canonical engine-backed
  surface
- `MemoryStorageAdapter`, `RedisStorageAdapter`, `createStorageAdapter`
  - their types (observability bucket per architect § 7.2)
- `ExpressSessionTracker`, `withSessionTracking`, `SessionData`,
  `AgentShieldRequest`

### Migration

Customers still importing the deleted symbols today were already
hitting throw-stubs at runtime; their code is broken either way.
The fix is the same as it was in 1.0.0 — replace the legacy
factory with `withCheckpoint`. See README "Migration" section.

Customers still on the `@kya-os/agentshield-express` package name
should install the final deprecation stub at
[`@kya-os/agentshield-express@0.2.2`](../legacy-deprecation/agentshield-express/),
which throws a migration message at import time.

### Tests

- New `e7-deletion-gate.test.ts` pins the deletion: file-existence
  absence + no stale imports anywhere in `src/` + canonical
  replacement still ships
- Removed `e4-deletion-gate.test.ts` (superseded — E.4 pinned the
  throw-stub contract; E.7 deletes the throw-stubs themselves)
- Removed `middleware.test.ts` (tested only deleted throw-stubs)

---

## 1.2.0 — 2026-05-18

**Minor release** — peer to `@kya-os/checkpoint-wasm-runtime@1.4.0`.
Bundler-port follow-through + Monitor-default visibility.

### Changed

- Collapsed the 5-line per-subpath externalize-wasm-runtime block in
  `tsup.config.ts` to a single `@kya-os/checkpoint-wasm-runtime`
  entry. The original block was a workaround for the `(void 0)`
  esbuild tree-shake bug fixed upstream in the wasm-runtime's
  bundler-target migration.

### Added

- Tier-3 Monitor-default visibility: a default-config Express install
  no longer blocks ChatGPT-User et al. unless the consumer writes a
  tenant-policy rule or opts into `tier3_action: 'block'`. The bench
  harness (bench-new-express) is the canonical opt-in example. See
  [wasm-runtime@1.4.0 CHANGELOG](../checkpoint-wasm-runtime/CHANGELOG.md)
  for the migration story.

---

## 1.1.3 — 2026-05-18

Companion patch for `@kya-os/checkpoint-wasm-runtime@1.2.0`.

### Changed

- Session persistence and enforcement compatibility logic now consume
  `VerifyResult.decision` plus `VerifyResult.detectionDetail`.

---

## 1.1.2 — 2026-05-17

**Critical rebuild.** 1.1.1 shipped with `engineVerify` bundled inline
from wasm-runtime@1.0.0 — which had the `(void 0) is not a function`
tree-shake bug that was later fixed in wasm-runtime@1.1.1 (#2611). Even
after that wasm-runtime fix, Express consumers were still hitting the
bug because the broken code was baked into Express's published bundle
rather than imported transitively at install time.

### Fix

tsup `external` now lists `@kya-os/checkpoint-wasm-runtime` (and its
subpath exports) so the wasm-runtime is loaded via Node's runtime
resolution rather than inlined at bundle time. Fresh installs of
checkpoint-express@1.1.2 transitively pull whatever wasm-runtime
version their dep range resolves — currently 1.1.2 (with all the
SDK-WASM-Bundler-Loader fixes from 1.1.0 → 1.1.2).

Bundle proof:

```
$ grep -c "function engineVerify" dist/index.mjs
0   # ✓ no longer inlined

$ grep "from '@kya-os/checkpoint-wasm-runtime" dist/index.mjs
import { verifyRequest, renderDecisionAsResponse } from '@kya-os/checkpoint-wasm-runtime/orchestrator';
import { makeSystemClock, ... } from '@kya-os/checkpoint-wasm-runtime/adapters';
```

### Surfaced by

Bench-Before-After-1 §3.8 pivot — when the Next.js bench-new path
proved unstable (see [#2618 SDK-Next.js-Integration-Audit-1](https://github.com/Know-That-Ai/agent-shield/issues/2618)),
the architect pivoted to Express. Local smoke against the freshly-
cloned `sites/express-bench-new` immediately hit `(void 0) is not a
function` in the bundled engineVerify — exposed Express's stale-bundle
issue that the Next.js path had hidden behind its own bundling layers.

### Followup

SDK-Next.js-Integration-Audit-1 (#2618) Option B (wasm-bindgen `--target
bundler`) will eventually let all consumers either bundle or externalize
wasm-runtime without the (void 0) class of bug. Until then, externalizing
at the consumer-package level is the right defense.

---

## 1.1.1 — 2026-05-17

Companion patch to `@kya-os/checkpoint-wasm-runtime@1.1.0`. Express
consumers can already use `withCheckpoint` (Next.js Turbopack was the
trigger, not Express's bundler-less Node import flow), but this bump
keeps the wasm-runtime dep range consistent across all checkpoint-\*
packages.

### Internal change

- `@kya-os/checkpoint-wasm-runtime` optional-dep changed from exact
  `1.0.0` pin to `^1.1.0` range (via `workspace:^`).

---

## 1.1.0 — 2026-05-17

Closes [SDK-Envelope-Plumbing-1 (#2594)](https://github.com/Know-That-Ai/agent-shield/issues/2594)
for Express. Coordinated release with `@kya-os/checkpoint-nextjs@1.1.0` —
identical config surface across both SDKs.

### New config (additive — no breaking changes)

- `CheckpointConfig.legacyEnvelopeFallback?: boolean` (default `false`) — accept
  envelopes via the legacy `KYA-Delegation` HTTP header alongside the canonical
  body form. Use for agents that pre-date Envelope-1 (#2537). Forwarded to
  the orchestrator's `VerifyRequestOpts.legacyEnvelopeFallback`.

### What's NOT in this release (and why)

Express's `translate.ts` already forwards `req.body` (as parsed by `body-parser`)
to the orchestrator, so the canonical `_meta.proof.jws` body transport has worked
since 1.0.0. No `drainJsonBody` config is needed — express consumers configure
body parsing via `express.json()` upstream of the middleware.

Express has a single middleware factory (`createCheckpointMiddleware` from
`./create-middleware`) which already calls `verifyRequest` through the
orchestrator — no factory-clarity migration needed (Next.js had a pattern-only
twin that competed with `withCheckpoint`; Express does not).

### Migration

No code changes required for existing 1.0.x consumers — `legacyEnvelopeFallback`
is optional and defaults to `false`. Consumers wanting to accept legacy header
envelopes:

```diff
  app.use(withCheckpoint({
    tenantHost: 'acme.checkpoint.example',
+   legacyEnvelopeFallback: true,
  }));
```

---

## 1.0.0 — 2026-05-16 (Phase E)

### Breaking

- **Renamed from `@kya-os/agentshield-express`.** Final deprecation
  stub published as `@kya-os/agentshield-express@0.2.2`; new home is
  this package.
- **Legacy local-detection chain retired.** `agentShield()`,
  `createAgentShieldMiddleware()`,
  `createEnhancedAgentShieldMiddleware()`, and `applyPolicy()` (and
  the rest of the `policy.ts` enforcement helpers) still export from
  this package — but now throw at runtime with a migration message
  pointing at `withCheckpoint`. The names survive so customers'
  imports don't `ImportError`; calls die loudly.
- Engine name surfaces as `checkpoint-engine-wasm` in the
  `X-Checkpoint-Engine` response header (engine self-identification;
  same value across all host runtimes — Next.js, Express, .NET,
  future hosts).

### Added

- **`withCheckpoint(config)`** — the canonical engine-backed
  middleware factory. Replaces every legacy entry point. Signature
  mirrors `@kya-os/checkpoint-nextjs`'s `withCheckpoint` exactly, so
  customers running both runtimes have a single mental model.
- `CheckpointConfig` type export — minimal config surface
  (`tenantHost`, `enforcementMode`, `argusUrl`, `dashboardUrl`,
  `reputationBaseline`, `adapters`, `onResult`).
- Cross-runtime parity at the response-adapter layer — the
  `__checkpoint_verdict` cookie is byte-equal to
  `@kya-os/checkpoint-nextjs` (shared `encodeVerdictCookie` primitive
  in `@kya-os/checkpoint-shared/host-adapter`).
- 19 tests across 4 files: 4 engine-backed smoke tests, 7 response-
  adapter parity tests (mirroring Phase D's adapt.test.ts shape), 5
  structural deletion-gate tests, 3 throw-stub contract tests.

### Preserved (per architect §7.2)

- `MemoryStorageAdapter`, `RedisStorageAdapter`, `createStorageAdapter`
  remain exported. Compose them with `withCheckpoint` via the
  `onResult` callback for session/event observability (see README).
- `session-helper.ts` cookie utilities unchanged.
- All policy-config and detection-result types still re-export from
  `@kya-os/checkpoint-shared` so customer config types keep
  type-checking through the migration window.

### Architecture

- Verification logic lives in exactly one place — the Rust
  `kya-os-engine` crate, compiled to WASM. This package is plumbing
  only: translate Express `Request` → `IncomingHttpLike`, call
  `verifyRequest`, adapt the verdict to the Express `Response`.
- `pnpm why @kya-os/agentshield*` from this package returns empty —
  zero transitive dep on the legacy crate names. Engine is the leaf
  crate; legacy crates are tombstones (final deprecation stubs under
  `packages/legacy-deprecation/`).

## 0.2.1 and earlier — legacy local-detection chain

The earlier versions shipped as `@kya-os/agentshield-express` with a
TS-side `AgentDetector` pattern matcher, local policy evaluation, and
combined detection + session-tracking + Redis storage in
`createEnhancedAgentShieldMiddleware`. See the final deprecation
stub at `@kya-os/agentshield-express@0.2.2` for migration notes.
