Merge branch 'stack/agent-profiles-5-web-ui' into stack/agent-profiles-8-authoring
The composer's preset seat stays removed here — this layer moved it to the hero chip and the session-header action — so only the model seat takes the `modelSeatLocked` narrowing master introduced. Conflicts: apps/web/tests/snapshots/*/*.expected.md packages/client/connection/README.md packages/client/connection/README.zh.md packages/client/ui-conversation/src/client/skeleton/InputBar.tsx packages/host/apiproxy/src/fetch/client.ts
This commit is contained in:
@@ -24,7 +24,8 @@
|
||||
},
|
||||
"dshClient": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-connection"
|
||||
"@deepseek-ai/dsh-client-connection",
|
||||
"@deepseek-ai/dsh-typert-registry"
|
||||
],
|
||||
"platform": "web",
|
||||
"immediately": true
|
||||
@@ -47,11 +48,15 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"@deepseek-ai/dsh-type-meta": "^0.0.1",
|
||||
"@deepseek-ai/dsh-typert-registry": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-timeout": "workspace:^",
|
||||
"@deepseek-ai/dsh-type-meta": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-registry": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
import { Context as CordisContext } from 'cordis'
|
||||
import type { Context, Fiber } from 'cordis'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { TypeRTClientRemote, TypeRTRemoteScopeApi } from '@deepseek-ai/dsh-type-meta'
|
||||
|
||||
/** Client Cordis Context carrying one Agent identity and its scoped Remote namespaces. */
|
||||
export type AgentContext = Omit<Context, 'remote'> & {
|
||||
readonly remote: TypeRTClientRemote & TypeRTRemoteScopeApi<'agent'>
|
||||
}
|
||||
|
||||
/** Context tag written by {@link createScope}. */
|
||||
const kScope = Symbol('dsh.client.scope')
|
||||
@@ -29,7 +35,7 @@ export interface AgentScopeHandle {
|
||||
* through it (passing it as the dispatch subject routes to this agent's
|
||||
* tagged listeners plus every untagged one).
|
||||
*/
|
||||
ctx: Context
|
||||
ctx: AgentContext
|
||||
/** Backing fiber (dispose tears down every scope-owned registration). */
|
||||
fiber: Fiber
|
||||
}
|
||||
@@ -48,15 +54,16 @@ function agentScope(): void {}
|
||||
*/
|
||||
export function createScope(ctx: Context, key: SessionId): AgentScopeHandle {
|
||||
const fiber = ctx.plugin(agentScope)
|
||||
const scoped = fiber.ctx.extend({
|
||||
[kScope]: key,
|
||||
[CordisContext.filter](listenerCtx: Context): boolean {
|
||||
const tag = scopeOf(listenerCtx)
|
||||
return tag === undefined || tag === key
|
||||
},
|
||||
}) as AgentContext
|
||||
return {
|
||||
fiber,
|
||||
ctx: fiber.ctx.extend({
|
||||
[kScope]: key,
|
||||
[CordisContext.filter](listenerCtx: Context): boolean {
|
||||
const tag = scopeOf(listenerCtx)
|
||||
return tag === undefined || tag === key
|
||||
},
|
||||
}),
|
||||
ctx: scoped,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
RpcResult, SessionId, SubagentAddress,
|
||||
} from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { HostObservable, SessionMaybeProvideInfo } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { AgentContext } from '../agents/scope.ts'
|
||||
import type { SessionSearchResultItem } from '../sessions/manager.ts'
|
||||
import type {
|
||||
SessionBinding, SessionListState, SessionProvideDescriptor,
|
||||
@@ -19,6 +20,8 @@ import type {
|
||||
import type { SessionFace } from './session.ts'
|
||||
import type { ObservableSnapshot } from './store.ts'
|
||||
|
||||
export type { AgentContext } from '../agents/scope.ts'
|
||||
|
||||
/** The sessions-service face injected as `ctx.sessions`. */
|
||||
export interface ISessions {
|
||||
/** The useSessions standard feed (list rows + current selection; read face — writes stay inside the domain). */
|
||||
@@ -95,7 +98,7 @@ export interface ISessions {
|
||||
* @param id - session id.
|
||||
* @returns scoped ctx, or undefined for a session neither listed nor already scoped.
|
||||
*/
|
||||
scope(id: SessionId): Context | undefined
|
||||
scope(id: SessionId): AgentContext | undefined
|
||||
/**
|
||||
* Read the Agent scope tag off a context (service-method seam: fetch
|
||||
* bundles must reach scope resolution through ctx.sessions).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/** Browser runtime services for slots, sessions, workspaces, and connection-stream delivery. */
|
||||
import type { Context } from 'cordis'
|
||||
import type { ConnectionHandle, SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { TypeRTContext } from '@deepseek-ai/dsh-type-meta'
|
||||
import type { MaybeSnapshotSelectorHook, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { SlotsService } from './slots.ts'
|
||||
import { SessionsService } from './sessions/service.ts'
|
||||
@@ -26,7 +27,7 @@ export type { ISession, ProjectionsFace, SessionFace } from './contract/session.
|
||||
export type {
|
||||
ISessionHistory, SessionHistoryFace, SessionHistorySnapshot,
|
||||
} from './contract/session-history.ts'
|
||||
export type { ISessions } from './contract/sessions.ts'
|
||||
export type { AgentContext, ISessions } from './contract/sessions.ts'
|
||||
export type { IWorkspaces } from './contract/workspaces.ts'
|
||||
export type {
|
||||
SessionBinding, SessionListState, SessionProvideContribution, SessionProvideDescriptor, SessionSummary,
|
||||
@@ -75,6 +76,13 @@ export type { SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
/** Client-side Cordis context after declaration merging. */
|
||||
export type ClientContext = Context
|
||||
|
||||
declare module '@deepseek-ai/dsh-type-meta' {
|
||||
interface TypeRTContextMap {
|
||||
/** Client Agent scope identity; the agent and session share one wire id. */
|
||||
agent: TypeRTContext<SessionId>
|
||||
}
|
||||
}
|
||||
|
||||
/** The conversation-snapshot selector hook (ConvViewProps/ToolRowProps take this). */
|
||||
export type UseConversationSession = SnapshotSelectorHook<ConversationSnapshot>
|
||||
|
||||
@@ -170,8 +178,8 @@ declare module 'cordis' {
|
||||
}
|
||||
}
|
||||
|
||||
/** Required services: the wire handle mounted by the connection plugin. */
|
||||
export const inject = ['connection']
|
||||
/** Required services: the wire handle and Client TypeRT registry. */
|
||||
export const inject = ['connection', 'typert']
|
||||
|
||||
/** Mounts the browser runtime services and connection stream.
|
||||
* @param ctx - Client Cordis context.
|
||||
@@ -180,6 +188,9 @@ export function apply(ctx: Context): void {
|
||||
ctx.plugin(SlotsService)
|
||||
const connection = ctx.get('connection') as ConnectionHandle
|
||||
const sessions = new SessionsService(ctx, connection.api)
|
||||
ctx.typert.contexts.registerClient('agent', {
|
||||
identity: candidate => sessions.scopeOf(candidate),
|
||||
})
|
||||
const sessionHistory = new SessionHistoryService(ctx, connection.api)
|
||||
const workspaces = new WorkspacesService(ctx, connection.api, sessions)
|
||||
ctx.effect(
|
||||
|
||||
@@ -29,7 +29,7 @@ import type { SessionProjectionMap } from '@deepseek-ai/dsh-session-projection/t
|
||||
import type { SnapshotStore } from '../contract/store.ts'
|
||||
import { createSnapshotStore } from '../contract/store.ts'
|
||||
import type { SessionFace } from '../contract/session.ts'
|
||||
import type { ISessions } from '../contract/sessions.ts'
|
||||
import type { AgentContext, ISessions } from '../contract/sessions.ts'
|
||||
import { createScope, scopeOf as scopeTagOf } from '../agents/scope.ts'
|
||||
import { SessionManager } from './manager.ts'
|
||||
import type { SessionListPhase, SessionSearchResultItem, SubagentCatalogSnapshot } from './manager.ts'
|
||||
@@ -133,7 +133,7 @@ export interface SessionBinding {
|
||||
readonly sessionId: SessionId
|
||||
/** The outward session face only — feature code never sees the concrete class. */
|
||||
readonly session: SessionFace
|
||||
readonly ctx: Context
|
||||
readonly ctx: AgentContext
|
||||
}
|
||||
|
||||
// Scope primitives live in ../agents/scope.ts (the client mirror of host
|
||||
@@ -188,7 +188,7 @@ function increasedForkTitle(title: string): string {
|
||||
|
||||
interface ScopeRecord {
|
||||
fiber: Fiber
|
||||
ctx: Context
|
||||
ctx: AgentContext
|
||||
binding: SessionBinding
|
||||
/** The concrete Session for runtime-internal entry points (staging open()); the binding carries only the outward face. */
|
||||
session: Session
|
||||
@@ -489,7 +489,7 @@ export class SessionsService implements ISessions {
|
||||
* @param id - session id (the agent identity — 1:1 same axis).
|
||||
* @returns scoped ctx, or undefined for a session neither listed nor already scoped.
|
||||
*/
|
||||
scope(id: SessionId): Context | undefined {
|
||||
scope(id: SessionId): AgentContext | undefined {
|
||||
return this.resolve(id)?.ctx
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { ConnectionSinks } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import { SESSION_SEARCH_RESULT_LIMIT } from '@deepseek-ai/dsh-host-apiproxy/api'
|
||||
import TypertRegistry from '@deepseek-ai/dsh-typert-registry'
|
||||
import * as RuntimeClient from '../src/client/index.ts'
|
||||
import type { SessionsService } from '../src/client/sessions/service.ts'
|
||||
import type { WorkspacesService } from '../src/client/workspaces/service.ts'
|
||||
@@ -22,17 +23,22 @@ interface Bench {
|
||||
|
||||
async function mount(): Promise<Bench> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(TypertRegistry)
|
||||
const api = new FakeApiClient()
|
||||
const bench: Bench = { ctx, api, sinks: undefined, stopped: 0 }
|
||||
const handle: ConnectionHandle = {
|
||||
api,
|
||||
isLoopback: true,
|
||||
rpc: {
|
||||
call: () => Promise.reject(new Error('unexpected generic RPC call')),
|
||||
},
|
||||
start: (sinks) => {
|
||||
bench.sinks = sinks
|
||||
return { stop: () => { bench.stopped += 1 } }
|
||||
},
|
||||
}
|
||||
ctx.reflect.provide('connection', handle)
|
||||
ctx.reflect.provide('remote', {})
|
||||
await ctx.plugin(RuntimeClient).await()
|
||||
return bench
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export class FakeApiClient implements IApiClient {
|
||||
|
||||
onModels: (payload: unknown) => Promise<RpcResponse<SessionModels>> = () => Promise.resolve(ok({
|
||||
current: this.defaultModel,
|
||||
routable: true,
|
||||
groups: [{
|
||||
id: 'deepseek-official',
|
||||
name: 'DeepSeek',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Context } from 'cordis'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { ConnectionHandle, ConnectionSinks } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import TypertRegistry from '@deepseek-ai/dsh-typert-registry'
|
||||
import * as RuntimeClient from '../src/client/index.ts'
|
||||
import { FakeApiClient } from './fake-api.ts'
|
||||
|
||||
@@ -16,17 +17,22 @@ interface Bench {
|
||||
|
||||
async function mount(): Promise<Bench> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(TypertRegistry)
|
||||
const api = new FakeApiClient()
|
||||
const bench: Bench = { ctx, sinks: undefined }
|
||||
const handle: ConnectionHandle = {
|
||||
api,
|
||||
isLoopback: true,
|
||||
rpc: {
|
||||
call: () => Promise.reject(new Error('unexpected generic RPC call')),
|
||||
},
|
||||
start: (sinks) => {
|
||||
bench.sinks = sinks
|
||||
return { stop: () => {} }
|
||||
},
|
||||
}
|
||||
ctx.reflect.provide('connection', handle)
|
||||
ctx.reflect.provide('remote', {})
|
||||
await ctx.plugin(RuntimeClient).await()
|
||||
return bench
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
},
|
||||
{
|
||||
"path": "../../typert/type-meta"
|
||||
},
|
||||
{
|
||||
"path": "../../typert/registry"
|
||||
}
|
||||
],
|
||||
"exclude": [
|
||||
|
||||
Reference in New Issue
Block a user