fix(typert): preserve remote lookup semantics
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/api-gateway.md
|
||||
api-gateway.md: 76af93880d278a17dc46370fd5065fdcdadb9fb6
|
||||
api-gateway.zh.md: d447cea6b64bf88084f86a210a5f654bd9445d6c
|
||||
api-gateway.md: 2e0717fd7b0e5b9ca33d650ffad7ac454046f780
|
||||
api-gateway.zh.md: 4d1beebf92cae702dac323cdd974b6220091a214
|
||||
|
||||
@@ -8,7 +8,7 @@ This is the current-state reference for the TypeRT API Gateway. It describes how
|
||||
|
||||
Business services use `@Remote` or `@RemoteContext` to select the methods exposed to the Client. Unmarked methods do not enter the generated Client types or runtime contributions and cannot be called through `ctx.api`.
|
||||
|
||||
`@Remote` denotes calling a Cordis service registered on the root Host Context. Complex Host objects cannot cross the wire directly; the business package must declare their association with a wire identity through `TypeRTLookupMap` and register a resolution provider with `ctx.typert.lookups` at runtime. For example, an `Agent` parameter named `agent` in the Host signature produces an `agentId` wire field, and the Gateway resolves that id to the current live object before invoking the business method.
|
||||
`@Remote` denotes calling a Cordis service registered on the root Host Context. Complex Host objects cannot cross the wire directly; the business package must declare their association with a wire identity through `TypeRTLookupMap` and register a default resolution provider with `ctx.typert.lookups` at runtime. For example, an `Agent` parameter named `agent` in the Host signature produces an `agentId` wire field, and the Gateway resolves that id to a Host object before invoking the business method. Host composition can use `ctx.typert.lookups.configure()` to override the resolution policy for a lookup key without changing the parameter name, wire field, or canonical type symbol owned by the business package.
|
||||
|
||||
`@RemoteContext(key)` first resolves an identity to a scoped Context through `ctx.typert.contexts`, then obtains the service from that Context and invokes the method. It applies when the method itself depends on scoped composition and does not need to receive objects such as `Agent` explicitly.
|
||||
|
||||
@@ -117,6 +117,8 @@ The Connection performs the unified trust check for `/api` before the HTTP bridg
|
||||
|
||||
For every call, the Gateway resolves the descriptor and live service from the current registries instead of caching business objects. It requires the fields in `args` to match the descriptor exactly, validates wire values with codecs, resolves objects or receivers through registered lookup or Context providers, invokes the service method targeted by the binding, and validates the return value. A missing provider, unknown identity, binding mismatch, missing or extra argument, schema failure, or missing method fails at the boundary before entering or after leaving business code.
|
||||
|
||||
The lookup provider's `register()` supplies both the stable declaration and the default resolver; `configure()` supplies a resolver owned by Host composition that may execute asynchronously and is scoped to an effect lifetime. Configuration may precede provider mounting; without a provider, invocation still fails with `lookup-unavailable`, and unloading the configuration restores the provider's default policy. The standard Web Host's API Proxy configures the same `agentFor()` semantics for `agent` and `session`: it reuses a live Agent, automatically resumes ordinary cold sessions, deduplicates concurrent resumes, and rejects identities owned by subagent routing; the `session` lookup returns that Agent's Session. Resume failures and ownership fences pass through unchanged as existing RPC errors rather than being collapsed into the Gateway's `internal` error.
|
||||
|
||||
Unloading a Client contribution removes its descriptors and concrete methods together, aborts its in-flight calls, and makes stale method handles retained by external code reject further calls. A strict endpoint withdrawn on the Host also does not degrade to SRC inference, preventing a hot unload from silently weakening validation.
|
||||
|
||||
## SRC development fallback
|
||||
@@ -155,3 +157,5 @@ The running Client watcher consumes these generated files when it rebundles; wit
|
||||
## Boundaries
|
||||
|
||||
Remote handles only unary method calls with one request and one result. Session event streams, pagination, incremental reduce, projection, and entity substreams require a separate data protocol and registration model; even when they reuse the Connection, they must not masquerade as Remote methods or enter invocation descriptors.
|
||||
|
||||
Lookup policy is currently configured per key, so all `agent` or `session` parameters share the cold-resume behavior. If a Remote endpoint must accept live objects only, an explicit per-parameter or per-endpoint policy must be added later; the business method must not guess whether the object came from restoration.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
业务 Service 通过 `@Remote` 或 `@RemoteContext` 选择对 Client 开放的方法。未标记的方法不会进入生成的 Client 类型或运行时贡献,也不能通过 `ctx.api` 调用。
|
||||
|
||||
`@Remote` 表示调用根 Host Context 中注册的 Cordis Service。复杂的 Host 对象不能直接跨 wire 传输;业务包必须通过 `TypeRTLookupMap` 声明它与 wire identity 的关联,并在运行时向 `ctx.typert.lookups` 注册解析提供方。例如 `Agent` 参数在 Host 签名中名为 `agent`,生成的 wire 字段为 `agentId`,Gateway 在调用业务方法前将 id 解析为当前的实时对象。
|
||||
`@Remote` 表示调用根 Host Context 中注册的 Cordis Service。复杂的 Host 对象不能直接跨 wire 传输;业务包必须通过 `TypeRTLookupMap` 声明它与 wire identity 的关联,并在运行时向 `ctx.typert.lookups` 注册默认解析提供方。例如 `Agent` 参数在 Host 签名中名为 `agent`,生成的 wire 字段为 `agentId`,Gateway 在调用业务方法前将 id 解析为 Host 对象。Host 组合可以用 `ctx.typert.lookups.configure()` 覆盖某个 lookup key 的解析策略,而不改变业务包拥有的参数名、wire 字段或规范类型 symbol。
|
||||
|
||||
`@RemoteContext(key)` 表示先通过 `ctx.typert.contexts` 把 identity 解析为一个作用域 Context,再从该 Context 取得 Service 并调用方法。它适用于方法本身依赖作用域组合、而不需要显式接收 `Agent` 等对象的情形。
|
||||
|
||||
@@ -117,6 +117,8 @@ Connection 在 HTTP bridge 之前执行 `/api` 的统一信任检查,再在共
|
||||
|
||||
Gateway 每次调用都从当前注册表解析描述符和实时 Service,不缓存业务对象。它要求 `args` 的字段集合与描述符完全一致,先用 codec 校验 wire 值,再通过注册的 lookup 或 Context provider 解析对象或接收者,最后调用 binding 指向的 Service 方法并校验返回值。缺少 provider、identity 未命中、binding 不一致、参数多缺、schema 失败和方法不存在都在进入或离开业务边界时失败。
|
||||
|
||||
lookup provider 的 `register()` 同时提供稳定声明和默认 resolver;`configure()` 提供由 Host 组合拥有、可异步执行且受 effect 生命周期约束的 resolver。配置可以先于 provider 挂载;没有 provider 时调用仍以 `lookup-unavailable` 失败,配置卸载后则恢复 provider 默认策略。标准 Web Host 的 API Proxy 为 `agent` 与 `session` 配置同一套 `agentFor()` 语义:复用 live Agent,自动恢复普通冷会话,对并发恢复去重,并拒绝由 subagent routing 拥有的 identity;`session` lookup 返回该 Agent 的 Session。恢复失败和 ownership fence 通过既有 RPC error 原样返回,不折叠为 Gateway 的 `internal` 错误。
|
||||
|
||||
Client 卸载一个贡献时会一起移除描述符和具体方法,中止其进行中的调用,并使外部仍持有的旧方法句柄拒绝继续调用。Host 上已经注册过的严格 endpoint 被撤回后也不会降级到 SRC 推断,以免热卸载悄然降低校验强度。
|
||||
|
||||
## SRC 开发回退
|
||||
@@ -155,3 +157,5 @@ pnpm run build:lib:contracts
|
||||
## 边界
|
||||
|
||||
Remote 只处理有单个请求与单个结果的一元方法调用。Session event stream、分页、增量 reduce、projection 和实体子流需要独立的数据协议与注册模型;即使它们复用 Connection,也不应伪装成 Remote 方法或放入调用描述符。
|
||||
|
||||
当前 lookup 策略按 key 配置,因此所有 `agent` 或 `session` 参数共享冷恢复行为。某个 Remote endpoint 若必须只接受 live 对象,需要后续增加显式的逐参数或逐 endpoint 策略,不能通过业务方法内部猜测恢复来源。
|
||||
|
||||
@@ -2593,7 +2593,7 @@ listPackages(filter: TypertPackageFilter = {}): TypertPackageRecord[]
|
||||
toJSONSchema(key: string, params?: z.core.ToJSONSchemaParams): z.core.JSONSchema.BaseSchema
|
||||
```
|
||||
|
||||
Source: [`packages/typert/registry/src/service.ts:346`](../../packages/typert/registry/src/service.ts)
|
||||
Source: [`packages/typert/registry/src/service.ts:400`](../../packages/typert/registry/src/service.ts)
|
||||
|
||||
## `ctx.typertGateway` — `TypertGatewayService`
|
||||
|
||||
@@ -2604,12 +2604,12 @@ Resolve strict generated definitions or conservative SRC markers against current
|
||||
* Invoke one live Remote method through strict generated reflection or SRC markers.
|
||||
* @param request - decoded endpoint and exact named wire arguments.
|
||||
* @returns the validated business result.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; business errors retain their identity.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
|
||||
*/
|
||||
async invoke(request: InvokeRemoteRequest): Promise<unknown>
|
||||
```
|
||||
|
||||
Source: [`packages/host/api-gateway/src/index.ts:76`](../../packages/host/api-gateway/src/index.ts)
|
||||
Source: [`packages/host/api-gateway/src/index.ts:78`](../../packages/host/api-gateway/src/index.ts)
|
||||
|
||||
## `ctx.userInteraction` — `UserInteractionService`
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/core-data-structures/typert.md
|
||||
typert.md: da6e229ff6a2300c36f5734ad05c621a5e63082d
|
||||
typert.zh.md: b3b0e8897756b5b4f9b645522cc5a1b27eac1d33
|
||||
typert.md: 1ff0fe80e483d481f686336c86038cdd169ecdbc
|
||||
typert.zh.md: 3cc0aa26406e01db5a6c05074210fc9d40b8ec00
|
||||
|
||||
@@ -114,7 +114,7 @@ interface InvocationDescriptor {
|
||||
|
||||
## TypeRT registry
|
||||
|
||||
`ctx.typert` separates current-environment descriptors, explicitly selected Remote contributions, live lookup providers, and scoped Context providers. Registrations are Cordis-owned effects and return awaitable disposers.
|
||||
`ctx.typert` separates current-environment descriptors, explicitly selected Remote contributions, lookup providers, and scoped Context providers. A lookup provider owns the stable wire declaration and default resolver; Host composition can configure an effect-scoped synchronous or asynchronous resolver for the same key, and unloading that configuration restores the default policy. Registrations are Cordis-owned effects and return awaitable disposers.
|
||||
|
||||
```ts type-equiv
|
||||
/** Minimal TypeRT runtime consumed through dependency inversion. */
|
||||
@@ -135,7 +135,7 @@ interface TypeRTRemoteNamespaceMap {}
|
||||
|
||||
## Host Gateway
|
||||
|
||||
Connection decodes its carrier envelope before calling `ctx.typertGateway`. The request carries exact named wire fields and the carrier's cancellation signal separately; infrastructure and boundary failures use the Gateway's in-process error taxonomy, although the current RPC adapter folds them into the transport's `internal` error code.
|
||||
Connection decodes its carrier envelope before calling `ctx.typertGateway`. The request carries exact named wire fields and the carrier's cancellation signal separately; infrastructure and boundary failures use the Gateway's in-process error taxonomy, ordinary exceptions are folded by the RPC adapter into the transport's `internal` error code, and existing RPC errors carried by lookup policy through `TypeRTLookupFailure` are returned unchanged.
|
||||
|
||||
```ts type-equiv
|
||||
/** One Remote method request after a carrier has decoded its envelope. */
|
||||
@@ -180,7 +180,7 @@ interface TypertGateway {
|
||||
* Invoke one live Remote method without assuming a carrier or response envelope.
|
||||
* @param request - decoded endpoint and named wire arguments.
|
||||
* @returns the validated business result.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; business errors retain their identity.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
|
||||
*/
|
||||
invoke(request: InvokeRemoteRequest): Promise<unknown>
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ interface InvocationDescriptor {
|
||||
|
||||
## TypeRT 注册表
|
||||
|
||||
`ctx.typert` 分开保存当前环境的 descriptor、显式选择的 Remote contribution、活 lookup 提供方与 scoped Context 提供方。各项注册都是由 Cordis 持有的 effect,并返回可等待的 disposer。
|
||||
`ctx.typert` 分开保存当前环境的 descriptor、显式选择的 Remote contribution、lookup 提供方与 scoped Context 提供方。lookup 提供方拥有稳定 wire 声明和默认 resolver;Host 组合可以为同一个 key 配置 effect-scoped 同步或异步 resolver,配置卸载后恢复默认策略。各项注册都是由 Cordis 持有的 effect,并返回可等待的 disposer。
|
||||
|
||||
```ts type-equiv
|
||||
/** Minimal TypeRT runtime consumed through dependency inversion. */
|
||||
@@ -135,7 +135,7 @@ interface TypeRTRemoteNamespaceMap {}
|
||||
|
||||
## Host Gateway
|
||||
|
||||
Connection 会先解码 carrier envelope,再调用 `ctx.typertGateway`。请求将精确的具名 wire 字段与 carrier 的取消 signal 分开携带;基础设施与边界失败使用 Gateway 的进程内错误分类体系,但当前 RPC 适配器会把这些错误折叠为传输层的 `internal` 错误码。
|
||||
Connection 会先解码 carrier envelope,再调用 `ctx.typertGateway`。请求将精确的具名 wire 字段与 carrier 的取消 signal 分开携带;基础设施与边界失败使用 Gateway 的进程内错误分类体系,普通异常由 RPC 适配器折叠为传输层的 `internal` 错误码,lookup 策略通过 `TypeRTLookupFailure` 携带的既有 RPC error 则原样返回。
|
||||
|
||||
```ts type-equiv
|
||||
/** One Remote method request after a carrier has decoded its envelope. */
|
||||
@@ -180,7 +180,7 @@ interface TypertGateway {
|
||||
* Invoke one live Remote method without assuming a carrier or response envelope.
|
||||
* @param request - decoded endpoint and named wire arguments.
|
||||
* @returns the validated business result.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; business errors retain their identity.
|
||||
* @throws {@link TypertGatewayError} for dispatch, provider, or boundary failures; lookup-policy and business errors retain identity.
|
||||
*/
|
||||
invoke(request: InvokeRemoteRequest): Promise<unknown>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user