Merge origin/master (SessionFace contract) into web-permission-sandbox-merge-master

Three ui-conversation spec conflicts resolve to master's SlotTestRuntime
rewrites. Adaptation to the new outward session face: ISession gains the
command verb (the composer chip and the /permission picker submit through
it), FixtureSession grows the matching fail-loud stub plus the
waitingApproval summary default, and the picker reads the projection
through projections.faceOf (the ProjectionsFace shape) instead of the
retired store getter.
This commit is contained in:
imccyu
2026-07-29 01:41:54 +08:00
323 changed files with 8217 additions and 3509 deletions
@@ -10,7 +10,7 @@
* the `/permission <preset>` command line, so both surfaces write through
* one path and the pushed projection frame is the one confirmation.
*/
import type { ClientContext, Session } from '@deepseek-ai/dsh-client-runtime/client'
import type { ClientContext, SessionFace } from '@deepseek-ai/dsh-client-runtime/client'
import type { CommandServiceContract, SelectOption } from '@deepseek-ai/dsh-client-ui-command/client'
import type { ClientSessionContext } from '@deepseek-ai/dsh-client-ui-slash/client'
import type { PermissionSelect } from '@deepseek-ai/dsh-permission/client'
@@ -19,8 +19,8 @@ import type { PermissionSelect } from '@deepseek-ai/dsh-permission/client'
export const inject = ['command', 'sessions']
/** Read one session's current permissions projection value (undefined = capability absent). */
function selectOf(session: Session | undefined): PermissionSelect | undefined {
return session?.projections.get('permissions') as PermissionSelect | undefined
function selectOf(session: SessionFace | undefined): PermissionSelect | undefined {
return session?.projections.faceOf('permissions').getSnapshot() as PermissionSelect | undefined
}
/** Flatten the projection select into popup rows; `custom` is display state, never a target. */
@@ -43,7 +43,7 @@ function optionsOf(value: PermissionSelect): SelectOption[] {
export function apply(ctx: ClientContext): void {
const command = ctx.get('command') as CommandServiceContract
const sessions = ctx.sessions
const sessionFor = (session: ClientSessionContext): Session | undefined =>
const sessionFor = (session: ClientSessionContext): SessionFace | undefined =>
sessions.binding(session.sessionId)?.session
ctx.effect(() => command.register({
name: 'permission',
@@ -38,7 +38,12 @@ async function bench() {
const commands: string[] = []
let commandResult: { ok: boolean; matched?: boolean } = { ok: true, matched: true }
const session = (id: SessionId) => ({
projections: { get: (key: string) => (key === 'permissions' ? values.get(id) : undefined) },
projections: {
faceOf: (key: string) => ({
getSnapshot: () => (key === 'permissions' ? values.get(id) : undefined),
subscribe: () => () => {},
}),
},
command: (line: string) => {
commands.push(line)
return Promise.resolve(commandResult.ok