Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ jobs:
- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal

- name: Test against Eve 0.27.6
- name: Test against the pinned Eve fixture
env:
EVE_PROBE_NO_BUILD: 1
run: npm run test:client --prefix test/fixtures/eve-agent
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
- name: Test
run: dotnet test --configuration Release --no-build --verbosity normal

- name: Test against Eve 0.27.6
- name: Test against the pinned Eve fixture
env:
EVE_PROBE_NO_BUILD: 1
run: npm run test:client --prefix test/fixtures/eve-agent
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ and structured-output support without using the TypeScript SDK.
- `EveStreamFollower` mirrors upstream absolute-index reconnect and idle-budget behavior.
- Preview event and inspection payloads retain raw `JsonElement` values so unknown
upstream fields and event types remain available.
- The compatibility baseline is Vercel eve 0.27.6, stream protocol version 19.
- The compatibility baseline is Vercel eve 0.29.4, stream protocol version 20.
- `test/fixtures/eve-agent` is a pinned, deterministic real Eve server used by the
compatibility probe under `tests/NexusLabs.Eve.CompatibilityProbe`.
- `version.json` is the only release-version source; package versions come from NBGV.
Expand Down
16 changes: 14 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,25 @@ and this project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
- `EveStreamOptions.Follow` for bounded catch-up reads that stop at the durable stream tail
observed when the stream opens, using the `includeTailIndex=1` query parameter and the
`x-eve-stream-tail-index` response header.
- `EveStreamEventMetadata.Id` projecting the stable `evt_`-prefixed identifier that
message-stream protocol `20` stamps on every persisted event, plus
`EveStreamEventDeduplicator` for dropping re-delivered events across reconnects and
rewinds. Events persisted under protocol `19` report `null` and are always admitted.
- `EveInputRequest.Kind` and `EveInputRequest.RawKind`, projecting eve's framework-owned
input-request discriminator through the `EveInputRequestKind` enum so `question`,
`tool-approval`, and `session-limit` requests are routed by contract instead of by
option shape. A server that predates the discriminator reports `Unknown` with a `null`
raw value.

### Changed

- The compatibility reference moved to eve `0.27.6` (message-stream protocol `19`).
- The compatibility reference moved to eve `0.29.4` (message-stream protocol `20`). The
pinned CI fixture runs that release, and the compatibility probe now verifies stamped
event identifiers, a real bounded catch-up read against the durable tail header, and an
approval-gated human-input pause end to end.
- Accepted session IDs and continuation tokens are persisted in `EveSession.State`
as soon as `SendAsync` returns, before the response stream is consumed.
- Non-protected per-request headers now override client-level values, matching eve `0.27.6`.
- Non-protected per-request headers now override client-level values, matching upstream eve.
Authentication-owned and explicitly protected headers remain authoritative by default and
require an allowlisted, dedicated per-call override.

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ health and agent inspection, authentication, durable sessions, human-input respo
cooperative cancellation, session reset, NDJSON streaming, reconnect-by-index,
attachments, and structured output.

The initial compatibility target is Vercel `eve` **0.27.6** at commit
`05f348023d4268c974c225c1189a283ace20b742`, whose message stream protocol is version
**19**. eve is still a preview, so pin and test compatible versions before upgrading.
The initial compatibility target is Vercel `eve` **0.29.4** at commit
`85c1dd7a647a04cc1bd74879ba8d27a3ba0bdd9d`, whose message stream protocol is version
**20**. eve is still a preview, so pin and test compatible versions before upgrading.

## Prerequisites

Expand Down
2 changes: 1 addition & 1 deletion docs/attachments-and-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ distinguishes them:
|---|---|---|
| `Question`, `ToolApproval`, `SessionLimit` | matching wire value | A modelled request kind |
| `Unknown` | the wire value | A newer eve emitted a kind this package does not model |
| `Unknown` | `null` | The server predates the discriminator, such as eve `0.27.6` |
| `Unknown` | `null` | The server predates the discriminator, such as eve `0.27.6` or earlier |

A `kind` that is present but not a string is a malformed request and throws
`EveProtocolException` rather than being reported as a legacy server.
2 changes: 1 addition & 1 deletion docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ credentials, even when that name is allowlisted.
## Migration

Existing consumers keep authentication-authoritative behavior by default. To opt into the
eve 0.27.6 identity-forwarding use case:
upstream eve identity-forwarding use case:

1. Identify the exact protected header that must be replaceable.
2. Add only that name to `AllowedProtectedHeaderOverrides`.
Expand Down
42 changes: 25 additions & 17 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,26 @@ description: Understand supported eve versions, stream protocol compatibility, a

| NexusLabs.Eve | Reference eve | Stream protocol | Status |
|---|---:|---:|---|
| 0.1.x | 0.27.6 | 19 | Primary compatibility target |
| 0.1.x | 0.29.4 | 20 | Primary compatibility target |
| 0.1.x | 0.27.6 | 19 | Previous baseline; tolerated, not gated by CI |
| 0.1.x | 0.24.6 | 19 | End-to-end verified with `bg-eve` |

eve remains preview software. Package upgrades should therefore validate both:

1. The public HTTP route and body contracts.
2. The durable message-stream protocol version and event shapes.

The repository contains a pinned eve `0.27.6` fixture with a deterministic
The repository contains a pinned eve `0.29.4` fixture with a deterministic
model. CI builds the real server and verifies health, info, text turns,
attachment staging, streaming, cooperative cancellation, and session reset
through the C# client.
attachment staging, streaming, bounded catch-up reads, cooperative cancellation,
approval-gated human input, and session reset through the C# client.

Upstream eve 0.27.6 lets generic per-request headers replace authentication.
The client stays readable against protocol 19 servers: durable event
identifiers and input-request discriminators are both projected as absent
rather than causing a failure. That path is covered by contract tests, not by
the pinned fixture.

Upstream eve lets generic per-request headers replace authentication.
NexusLabs.Eve requires an explicit client allowlist and dedicated per-call override
for protected headers so existing generic header bags cannot silently replace credentials.

Expand All @@ -31,9 +37,9 @@ instead of causing deserialization failure.
Stream protocol version 20 stamps every persisted event with a stable
`evt_`-prefixed identifier. `EveStreamEvent.Metadata.Id` projects it when
present and reports `null` for events persisted under earlier protocol
versions, which cannot be deduplicated. eve `0.27.6` emits protocol version 19,
so the pinned compatibility probe asserts that durable timestamps still arrive
while identifiers stay absent.
versions, which cannot be deduplicated. The compatibility probe asserts that
the pinned server stamps a well-formed identifier on every event of a turn and
never repeats one.

## Upstream parity radar

Expand All @@ -50,11 +56,13 @@ deliberately not committed.

Bounded catch-up reads (`EveStreamOptions.Follow = false`) depend on the
`includeTailIndex=1` stream query parameter and the `x-eve-stream-tail-index`
response header. eve `0.27.6` accepts the query parameter but does not report the
header, so bounded reads against that baseline fail with `EveProtocolException`
instead of silently degrading to a live follow. The compatibility probe asserts
both halves of that contract and switches to verifying a real bounded read once
the pinned server reports the header.
response header. The pinned server reports the header, so the compatibility
probe verifies a real bounded read: the first request asks for the tail,
reconnects never re-request it, and the read stops exactly at the durable bound
while advancing the stored cursor. A server that omits the header, or reports a
malformed or out-of-range value, fails with `EveProtocolException` instead of
silently degrading to a live follow. eve `0.27.6` accepted the query parameter
without reporting the header, so bounded reads against that release fail.

## Input request kinds

Expand All @@ -63,7 +71,7 @@ eve stamps each human-input request with a framework-owned `kind` of `question`,
`EveInputRequest.RawKind` preserves the wire value, so an unmodelled future kind
stays inspectable instead of being misclassified from its option shape.

eve `0.27.6` predates the discriminator and omits it, which reports
`EveInputRequestKind.Unknown` with a `null` raw value. The compatibility probe
drives a real approval-gated tool against the pinned fixture and asserts that
behavior end to end.
The compatibility probe drives a real approval-gated tool against the pinned
fixture, asserts the request arrives as `tool-approval`, answers it, and
verifies the turn resumes. A server that predates the discriminator reports
`EveInputRequestKind.Unknown` with a `null` raw value.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ part of the .NET API.

## Compatibility

The initial release targets eve `0.27.6`, message-stream protocol version `19`.
The initial release targets eve `0.29.4`, message-stream protocol version `20`.
The client has also completed an end-to-end session against a real eve `0.24.6`
application using the same protocol version.

Expand Down
3 changes: 1 addition & 2 deletions docs/streaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ retried step is not a replay: it is emitted again under a new identifier.
Events persisted before protocol version 20 carry no identifier and report
`null`, so they cannot be deduplicated. eve `0.27.6` emits protocol version 19
and never stamps one.

`EveStreamEventDeduplicator` encodes that contract, so a caller that resumes a
stream can drop events it already processed:

Expand Down Expand Up @@ -108,7 +107,7 @@ Bounded reads require a nonnegative effective start cursor, so combining
`Follow = false` with a tail-relative `StartIndex` throws
`ArgumentOutOfRangeException`. A server that omits the tail header, or reports a
malformed or out-of-range value, throws `EveProtocolException`; eve `0.27.6`
does not report the header yet.
never reported the header, so bounded reads against that release fail.

## Bound individual events

Expand Down
4 changes: 2 additions & 2 deletions src/NexusLabs.Eve/EveProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ public static class EveProtocol
/// <summary>
/// Gets the upstream TypeScript package version used as the compatibility reference.
/// </summary>
public const string ReferenceEveVersion = "0.27.6";
public const string ReferenceEveVersion = "0.29.4";

/// <summary>
/// Gets the durable message-stream protocol version used by the reference client.
/// </summary>
public const string MessageStreamVersion = "19";
public const string MessageStreamVersion = "20";

/// <summary>
/// Gets the media type returned by eve session streams.
Expand Down
72 changes: 46 additions & 26 deletions test/fixtures/eve-agent/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions test/fixtures/eve-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"test:client": "node run-client-probe.mjs"
},
"dependencies": {
"ai": "7.0.34",
"eve": "0.27.6",
"ai": "7.0.42",
"eve": "0.29.4",
"just-bash": "3.1.0"
},
"engines": {
Expand Down
10 changes: 8 additions & 2 deletions test/fixtures/eve-agent/run-client-probe.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { once } from "node:events";
import { spawn } from "node:child_process";
import { rm } from "node:fs/promises";
import { readFile, rm } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";

const fixtureDirectory = path.dirname(fileURLToPath(import.meta.url));
const repositoryRoot = path.resolve(fixtureDirectory, "..", "..", "..");
const eveBin = path.join(fixtureDirectory, "node_modules", "eve", "bin", "eve.js");
const eveVersion = JSON.parse(
await readFile(
path.join(fixtureDirectory, "node_modules", "eve", "package.json"),
"utf8",
),
).version;
const probeProject = path.join(
repositoryRoot,
"tests",
Expand Down Expand Up @@ -79,7 +85,7 @@ try {
if (code !== 0) {
throw new Error(`The C# compatibility probe exited with code ${code}.`);
}
console.log("Eve 0.27.6 compatibility probe passed.");
console.log(`Eve ${eveVersion} compatibility probe passed.`);
} finally {
await stopProcess(server);
}
Expand Down
Loading