feat(remote): deliver allowlisted Host events through ctx.remote.$on

api/remotes owns the allowlist and its type projection; type-meta owns the shape
predicate, the selection seat, and the internal remote/host-event carrier
signal; api/gateway's Client half turns that signal into $on callbacks through a
private dispatch. apiproxy forwards each allowlisted emission verbatim in one
host/remote-event frame, registered ahead of the derived invalidation frames so
frame order is unchanged, and drops the three per-event variants it replaces.
Owner packages move their Events declarations into client-safe ./types exports,
so a consumer's listener signature is the Host's own declaration.
This commit is contained in:
imccyu
2026-08-10 21:32:50 +08:00
parent b64da061a8
commit d88f771e19
59 changed files with 956 additions and 257 deletions

View File

@@ -12,6 +12,7 @@ import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest'
import { SlotsService, type SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
import type { CommandDecoration } from '@deepseek-ai/dsh-client-ui-command/client'
import type { PermissionSelect } from '@deepseek-ai/dsh-permission/client'
import {
@@ -37,6 +38,9 @@ async function bench() {
const locale = new LocaleService(ctx)
locale.setLocale('en')
ctx.provide('locale', locale)
// The plugin injects `remote`; forwarded events reach it through the same
// `remote/host-event` signal the connection sink republishes.
new TestRemote(ctx)
ctx.slots.register({
name: 'root',
children: {
@@ -158,8 +162,8 @@ describe('ui-permission browser plugin', () => {
it('disposal removes the decoration (HMR safety)', async () => {
const b = await bench()
expect(b.decoration()).toBeDefined()
b.ctx.emit('settings/changed', 'another')
b.ctx.emit('settings/changed', 'permission')
b.ctx.emit('remote/host-event', 'settings/document-updated', ['another', 1])
b.ctx.emit('remote/host-event', 'settings/document-updated', ['permission', 1])
b.ctx.emit('connection/reset')
await b.fiber.dispose()
expect(b.decoration()).toBeUndefined()