- Add from_typemap.ts with SchemaAdapter interface, defaultAdapter, zodAdapter(), valibotAdapter()
- OperationRegistry now accepts optional { schemaAdapter } config; default is TypeBox passthrough
- Adapters use @alkdev/typemap (optional peer dep) with lazy init; detect schemas via ~standard vendor protocol
- Fix scanner pathToFileURL to encode URI components and normalize Windows backslashes
- Add from-typemap sub-path export in package.json and tsup config
- Add test suite covering defaultAdapter, zodAdapter, valibotAdapter, and registry integration
Add remote subscription support so spokes can consume streaming operations
over pubsub transports (WebSocket, Redis). Extract checkAccess to access.ts
to break circular dep between call.ts and subscribe.ts.
Remove outdated 'direct mode' terminology from test descriptions
since there is now only one invocation path (registry.execute).
The callMap option was removed from buildEnv() in previous tasks.
- Remove separate spec lookup, handler lookup, access control, and input validation from buildCallHandler
- Call registry.execute() directly; access control enforced via execute() (trusted not set)
- On error, look up spec for errorSchemas and pass to mapError()
- Make callMap required in CallHandlerConfig (no longer optional)
- Update tests: remove no-callMap tests, use callMap for all handler tests
- Add test for mapError with spec errorSchemas
- All 226 tests passing
- Add access control to registry.execute(): checks requiredScopes, requiredScopesAny,
and resourceType/resourceAction; rejects with ACCESS_DENIED when identity required
but absent; skips when context.trusted is true
- Add trusted field to OperationContext schema (internal, set by buildEnv for
nested calls to skip redundant scope checks)
- Simplify CallHandler to thin adapter: delegates to registry.execute() instead of
duplicating lookup, validation, and access control
- Remove callMap option from buildEnv(): always uses execute(), propagates context
with trusted: true for nested calls
- Add access control to subscribe(): same default-deny logic as execute()
- Change execute() to throw CallError instead of plain Error for not found,
no handler, and validation errors
- Export checkAccess from call.ts and index.ts for external use
- Remove CallMap type export, update EnvOptions
- Update architecture docs: api-surface.md, call-protocol.md,
ADR-006 status to implemented, source vs spec drift sections
- All 228 tests passing
- Pass context.identity through to callMap.call() in call protocol mode
for proper identity propagation in nested operation calls
- Add identity propagation test coverage for both with-identity and
without-identity scenarios
- Add test for parentRequestId propagation through callMap
- Add test for PendingRequestMap as callMap integration
- Add test for pre-built ResponseEnvelope pass-through in direct mode
- Add test for Value.Cast normalization via execute in direct mode
- Add test for empty registry, namespace grouping, and local source
metadata verification
- All 216 tests passing, build and lint clean
- CallEventSchema['call.responded'].output changed from Type.Unknown() to ResponseEnvelopeSchema
- PendingRequestMap.respond() now validates output with isResponseEnvelope(), throws on raw values
- PendingRequestMap.call() return type changed from Promise<unknown> to Promise<ResponseEnvelope>
- CallHandler captures handler return value instead of discarding it
- CallHandler applies result pipeline: detect envelope → wrap with localEnvelope → normalize with Value.Cast → validate with collectErrors
- CallHandler publishes call.responded via callMap.respond() with the envelope
- CallHandler publishes call.error via callMap.emitError() when callMap is provided (instead of re-throwing)
- CallHandlerConfig changed from eventTarget? to callMap? (PendingRequestMap)
- Adapter handlers pass through via isResponseEnvelope() detection (mcpEnvelope/httpEnvelope)
- All 189 tests passing, including 23 new tests for envelope behavior
- Change subscribe() return type to AsyncGenerator<ResponseEnvelope, void, unknown>
- Check isResponseEnvelope() on each yielded value: pass through if already an envelope
- Wrap raw values with localEnvelope(value, operationId) with fresh timestamp per yield
- Preserve generator cleanup (generator.return()) in finally block
- Preserve existing spec/handler not-found error behavior
- Add 13 tests covering wrapping, passthrough, timestamps, mixed yields, early termination
- Add mapMCPContentBlocks() helper mapping SDK ContentBlock[] to MCPContentBlock[]
- Extract tool.outputSchema via FromSchema() when present, fall back to Type.Unknown()
- Handler returns mcpEnvelope() with structured/legacy data path
- structuredContent preferred as data when present, Value.Cast() when outputSchema is known
- isError: true wrapped in envelope meta, NOT thrown
- Transport-level config errors throw CallError
- Unknown MCP content block types fall back to { type: 'text', text: JSON.stringify(block) }
- Add 20 tests for mapMCPContentBlocks and envelope detection
The resource access check in checkAccess() was bypassed when identity.resources
was undefined because the condition evaluated to false, falling through to .
Changed to with an explicit
check inside the block, implementing
default-deny semantics per ADR-006.
Added 7 test cases covering:
- undefined resources with resourceType set (denied)
- empty resources with resourceType set (denied)
- non-matching resource type (denied)
- matching type but wrong action (denied)
- matching type and action (granted)
- no resourceType/resourceAction set (granted)
- matching resources with extra scopes (granted)
- Split OperationRegistry into separate specs and handlers maps
- Add registerSpec(), registerHandler(), getHandler() methods
- register() still accepts IOperationDefinition (backward compatible)
- execute() now requires both spec and handler, throws if missing
- Update @alkdev/pubsub integration for v0.1.0 API:
- subscribe(type, id) now requires id parameter (use for all events)
- publish(type, id, payload) now requires 3 args
- Events unwrapped from EventEnvelope via .payload
- Update buildCallHandler to use getSpec() + getHandler() separately
- Update subscribe.ts to use getHandler()
- Update buildEnv to use getAllSpecs() instead of list()
- Update scanner to validate against OperationSpecSchema
- Update from_mcp and from_openapi to use OperationSpec & { handler } types
- Remove OperationDefinitionSchema from public exports
- Add 7 new registry tests for handler separation
Extracted from alkhub_ts packages/core/operations/ and packages/core/mcp/.
- Runtime-agnostic (injected fs/env deps, no Deno globals)
- Direct @logtape/logtape import instead of logger wrapper
- PendingRequestMap with pubsub-wired call protocol
- Peer-dep isolation for MCP adapter (sub-path export)
- Schema const naming convention (XSchema + X type alias)
- 68 tests passing, build + lint + test all green