Status: Developer preview — the SDK (@nexislabs/ai-platform) is source-available in the Praxa repo and contract-stable; npm publication follows the public API launch and its security review.
@nexislabs/ai-platform is the runtime-neutral contracts and streaming-client package for the Praxa Execution Fabric’s v1 chat protocol. It intentionally contains only:
- Versioned request, event, and RFC 9457-style error schemas (Zod).
- A Fetch/
ReadableStreamstreaming client with no Expo, React, React Native, Supabase, model-provider, database, or tool-execution dependency. - Strict parsing of every streamed event.
- No production endpoint serves this protocol yet. The existing Expo
/api/chatpath remains the app’s authoritative chat entry point today. - The package is version-locked at
0.1.0, ESM-only ("type": "module", no CommonJS build), and declares"engines": { "node": ">=20" }.
Installing
At launch:src/lib/platform-corpus/) resolve the @nexislabs/ai-platform specifier through a TypeScript path alias in the repo’s root tsconfig.json that points directly at packages/ai-platform/src/index.ts — there is no installed node_modules/@nexislabs package, and this alias is not a distribution mechanism. Outside that repo, the only way to use the SDK today is to vendor or path-import packages/ai-platform/src/index.ts directly.
Constructing the client
AiPlatformClientOptions:
The constructor does no I/O. The
baseUrl and fetch checks are synchronous and local; accessToken isn’t invoked until the first streamChat() call.
Making a turn-mode call
The only request this client makes today is thev1 turn-mode chat call: one POST {baseUrl}/v1/chat, one text/event-stream response, consumed as an AsyncGenerator<AiPlatformEventV1>. (The Praxa Execution Fabric design also defines a durable task mode over /v1/execute and /v1/runs/:id; that surface is not implemented in this package — see streaming.md.)
AiPlatformChatRequestV1Schema.parse(...) yourself first is optional — streamChat() calls it internally on every call — but doing it at the call site surfaces validation errors, and Zod’s applied defaults, before you’ve built UI state around the request. The schema is .strict(): an unknown top-level field (a typo, a stray userId) throws a ZodError synchronously, before any network call. There is deliberately no userId field — the caller’s identity comes from the access token, not the request body.
Fields that default when omitted
apiVersion and requestId have no default and are required on every call. Every identifier-shaped field (requestId, sessionId, conversationId, organizationId, message/file/tool-call/approval/memory ids, idempotencyKey) must match ^[A-Za-z0-9][A-Za-z0-9._:-]*$, 1–128 characters long.
Type exports
AiPlatformClientKindSchema, AiPlatformMessageSchema, and AiPlatformMessagePartSchema are exported as schemas only — the package does not re-export an inferred type for them the way it does for the request/event/problem/error-code schemas. Derive one yourself if you need it as a standalone type:
Schema-parsing guarantees
Every eventAiPlatformClient.streamChat() yields has passed two layers of validation:
- Schema validity, enforced by
parseAiPlatformEventStream(): each SSEdata:payload isJSON.parsed and then run throughAiPlatformEventV1Schema.parse(). Every event variant is.strict(), so an unknown extra field (a straychainOfThought, for instance) fails the same way an unrecognizedtypedoes — closed by construction, not by convention. A malformed or unrecognized event throws aZodErrorout of the generator; it is never silently dropped or passed through. - Stream-level invariants, enforced only by
AiPlatformClient.streamChat()on top of (1):event.requestIdmust equal the request’srequestId— otherwiseError("AI platform stream requestId mismatch").event.sequencemust strictly increase from the previous event — otherwiseError("AI platform stream sequence is not increasing").- No event may follow a terminal event (
response.final,request.cancelled,request.failed) — otherwiseError("AI platform stream emitted an event after termination"). - The stream must end on a terminal event — a clean EOF without one throws
Error("AI platform stream ended without a terminal event").
parseAiPlatformEventStream is exported standalone and only provides guarantee (1). Use it directly if you’re driving your own transport (a proxy, a recorded fixture, a non-fetch HTTP client) and still want strict per-event schema parsing. Use AiPlatformClient.streamChat() when you want the full guarantee set, including the requestId/sequence/termination checks that make a stream trustworthy enough to drive UI or billing state.
The SSE framing itself tolerates arbitrary chunk boundaries — a data: payload split across multiple network reads is reassembled before parsing — ignores any line that doesn’t start with data: (comments, keepalives), joins multi-line data: blocks with \n before parsing, and treats a literal data: [DONE] payload as an end-of-stream sentinel that is consumed, not yielded.
Continue with streaming.md for the full event union and approval handling, and errors-and-retries.md for the error, retry, and cancellation contract.