@ego-z/contracts
v0.11.2
Published
Wire-format type contracts shared between EgoZ backend, SDK, MCP and console. Type-only — no runtime artifacts.
Maintainers
Readme
@ego-z/contracts
Wire-format TypeScript contracts shared by every EgoZ surface — backend,
SDK (@ego-z/client), MCP server, and console. Types only, no runtime
exports. Published to npm as @ego-z/contracts so every surface can pin the same version.
Why this exists
The answer === undefined bug in @ego-z/[email protected] was caused by the
SDK's response decoder reading data.response while the backend was
writing data.answer. Two parallel hand-maintained interface sets in two
packages, drifting silently — exactly the failure mode this package is
designed to make impossible.
Anything that crosses the network between EgoZ components is declared
here once. Backend response builders and SDK decoders both reference the
same AskResponseData interface, so a field rename is a one-edit change
that the TypeScript compiler enforces across the stack.
Why types-only
import type { … } from '@ego-z/contracts' is fully erased by tsc /
tsup. That means:
- Zero runtime cost. No JS file is ever required. End users of
@ego-z/clientsee no@ego-z/contractsin theirnode_modules. - No path-alias-at-runtime headaches. Path resolution only matters at
compile time;
distoutput has no leftover references. - No publish. This package is
private: trueand is consumed via tsconfigpathsonly.
If you need a runtime constant (e.g. SDK_RESERVED_HEADERS,
HEADER_FORWARD_BLOCKLIST), keep it in the package that actually uses it
and add a // must mirror @ego-z/contracts comment if it shadows a contract.
How to consume it
In any package that needs the wire types, add to its tsconfig.json:
{
"compilerOptions": {
"paths": {
"@ego-z/contracts": ["../egoz-contracts/src"]
}
}
}Adjust the relative path if the package isn't a sibling of egoz-contracts/.
Then in code:
import type { AskRequestBody, AskResponseData, AskStreamEvent } from '@ego-z/contracts';tsup-built packages (@ego-z/client) inline the imported types into
their generated .d.ts automatically — end users see the types as if
they were declared in the SDK itself.
When to bump
- Adding an optional field — non-breaking, no version bump required (still good practice to bump the minor).
- Renaming or removing a field — breaking, bump major and ship a coordinated update of backend + SDK + MCP in one release.
- Adding a new SSE event variant to
AskStreamEvent— non-breaking for producers (backend just starts emitting it), breaking for consumers who exhaustively switch onevent.typeand rely onneverchecks. Bump the minor and call it out in the release notes.
What's in scope today
EgozApiResponse<T>envelope/askrequest + response (AskRequestBody,AskResponseData)/ask/streamrequest + full SSE event union- Common building blocks:
Intent,ResponseFormat,TokenUsage,JsonSchemaDefinition
What's not in scope yet (planned, see TODOs in each module):
- Tool CRUD wire shapes (
/tools/*) - RAG document wire shapes (
/rag/*) - API key / MCP token wire shapes (
/api-keys/*,/mcp-tokens/*) - Tenant / project wire shapes
These will be migrated incrementally as we touch them.
