Commit Graph

19 Commits

Author SHA1 Message Date
92936f4232 feat: implement ADR-007 subscription transport — PendingRequestMap.subscribe(), CallHandler dispatch, SSE AsyncGenerator handlers
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.
2026-05-16 06:03:21 +00:00
f534004615 feat(unified-env-remove-callmap): clean up 'direct mode' references in env tests
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.
2026-05-11 03:29:28 +00:00
95d9b95d13 feat(unified-callhandler): simplify CallHandler to delegate to registry.execute()
- 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
2026-05-11 03:19:26 +00:00
e138866fcd feat(unified-execute): implement ADR-006 unified invocation path with access control
- 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
2026-05-11 03:04:19 +00:00
d74b750ecb feat(env): complete envelope integration for buildEnv - propagate identity in call protocol mode and add comprehensive tests
- 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
2026-05-11 02:37:44 +00:00
b63c5ce3de Merge registry-envelope-integration into main (resolve conflicts with call-envelope-integration) 2026-05-11 02:23:52 +00:00
3150a49578 feat(registry-envelope-integration): update execute(), call, subscribe, env to return ResponseEnvelope
- OperationRegistry.execute() now returns Promise<ResponseEnvelope<TOutput>>
- Applies shared result pipeline: detect → wrap → normalize → validate
- Uses KindGuard.IsUnknown() to check if Value.Cast should be applied
- PendingRequestMap.call() returns Promise<ResponseEnvelope>
- PendingRequestMap.respond() validates envelope via isResponseEnvelope()
- CallHandler captures handler result, wraps, normalizes, validates, publishes
- CallEventSchema call.responded.output changed to ResponseEnvelopeSchema
- subscribe() yields ResponseEnvelope with isResponseEnvelope() passthrough
- OperationEnv inner functions return Promise<ResponseEnvelope>
- Tests updated for all new return types and behaviors
- 171 tests passing, build and lint clean
2026-05-11 02:19:05 +00:00
58a4d06be0 Merge branch 'feat/call-envelope-integration' 2026-05-11 02:15:59 +00:00
bf6d07938c feat(call-envelope-integration): integrate ResponseEnvelope into call protocol
- 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
2026-05-11 02:14:01 +00:00
6290feffb0 feat(subscribe): wrap yields in ResponseEnvelope
- 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
2026-05-11 02:06:20 +00:00
e111e1b4d8 Merge branch 'feat/mcp-envelope-integration' 2026-05-11 02:00:22 +00:00
b0283aa662 feat(mcp-envelope-integration): update from_mcp adapter to use mcpEnvelope, structuredContent, and outputSchema extraction
- 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
2026-05-11 01:59:30 +00:00
dc34eb29ea Merge branch 'feat/response-envelope-tests' 2026-05-11 01:57:06 +00:00
1d754b7d0a feat(response-envelope): add comprehensive tests for envelope types, factories, detection, schemas, and unwrap 2026-05-11 01:56:32 +00:00
854d5c7f3f Merge branch 'feat/openapi-envelope-integration' 2026-05-11 01:55:26 +00:00
c2c640f480 feat(openapi-envelope): update from_openapi handler to use httpEnvelope and CallError
Handler returns httpEnvelope(data, { statusCode, headers, contentType }) instead of raw response data.
HTTP errors throw CallError('EXECUTION_ERROR', ...) instead of plain Error.
Added tests for envelope wrapping and CallError behavior.
2026-05-11 01:52:09 +00:00
ac28c9308c fix(checkAccess): deny access when resourceType set but identity.resources undefined
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)
2026-05-11 01:50:12 +00:00
4f11f8e7a0 Separate handler from spec in OperationRegistry, update pubsub API
- 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
2026-05-09 08:25:59 +00:00
29f0dd7af0 Initial package implementation: operations registry, call protocol, and adapters
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
2026-04-30 12:34:26 +00:00