diff --git a/packages/api/session-controller/src/client/contract/result.ts b/packages/api/session-controller/src/client/contract/result.ts index 6577b63823..5306e611de 100644 --- a/packages/api/session-controller/src/client/contract/result.ts +++ b/packages/api/session-controller/src/client/contract/result.ts @@ -1,10 +1,11 @@ -/** Client operation results spanning Session Remote calls and the legacy subagent carrier. */ +/** Client operation results spanning the Session and subagent Remote calls. */ import type { RpcError } from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentControlError } from '@deepseek-ai/dsh-subagent/client' import type { SessionError } from '../../types.ts' /** Failure surfaced by the Client Session object layer. */ -export type ClientFailure = RpcError | SessionError +export type ClientFailure = RpcError | SessionError | SubagentControlError /** Success or failure returned by a Client Session operation. */ export type ClientResult = @@ -13,7 +14,7 @@ export type ClientResult = /** * Fold a rejected carrier operation into the Client Session failure vocabulary. - * @param error - rejection from a legacy subagent or local carrier call. + * @param error - rejection from a Remote or local carrier call. * @returns the failure branch of a Client Session result. */ export function transportResult(error: unknown): ClientResult { diff --git a/packages/api/session-controller/src/client/contract/sessions.ts b/packages/api/session-controller/src/client/contract/sessions.ts index 6520e5fa08..7c8a90e662 100644 --- a/packages/api/session-controller/src/client/contract/sessions.ts +++ b/packages/api/session-controller/src/client/contract/sessions.ts @@ -5,7 +5,7 @@ * explicit act of widening what features may do to the sessions domain. */ import type { Context } from '@deepseek-ai/cordis' -import type { SubagentAddress } from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types' import type { AgentContext } from '../scope.ts' diff --git a/packages/api/session-controller/src/client/contract/snapshot.ts b/packages/api/session-controller/src/client/contract/snapshot.ts index dff2317359..3f5f4ebd1c 100644 --- a/packages/api/session-controller/src/client/contract/snapshot.ts +++ b/packages/api/session-controller/src/client/contract/snapshot.ts @@ -2,7 +2,7 @@ import type { ContentBlock } from '@deepseek-ai/dsh-llm/types' import type { MessageId } from '@deepseek-ai/dsh-llm/brand' import type { SessionId } from '@deepseek-ai/dsh-session/types' -import type { SubagentAddress } from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { ClientFailure } from './result.ts' /** One transient inbox occurrence from the authoritative queue snapshot. */ diff --git a/packages/api/session-controller/src/client/index.ts b/packages/api/session-controller/src/client/index.ts index 7fa6ede0d0..10a10261bf 100644 --- a/packages/api/session-controller/src/client/index.ts +++ b/packages/api/session-controller/src/client/index.ts @@ -73,6 +73,7 @@ export const inject = [ 'remote', 'remote.commands', 'remote.session', + 'remote.subagents', ] /** @@ -82,7 +83,7 @@ export const inject = [ export function apply(ctx: Context): void { const connection = ctx.get('connection') as ConnectionHandle const remotes = ctx.remote as unknown as SessionRemotes - const sessions = new ClientSessions(ctx, connection.api, remotes) + const sessions = new ClientSessions(ctx, remotes) ctx.remote.$on('api-session/added', (summary) => { sessions.handleSessionAdded(summary) }) ctx.remote.$on('api-session/removed', (sessionId) => { sessions.handleSessionRemoved(sessionId) }) ctx.remote.$on('api-session/status', (sessionId, running) => { diff --git a/packages/api/session-controller/src/client/sessions/manager.ts b/packages/api/session-controller/src/client/sessions/manager.ts index 1a76102eaa..9ad6d88f96 100644 --- a/packages/api/session-controller/src/client/sessions/manager.ts +++ b/packages/api/session-controller/src/client/sessions/manager.ts @@ -2,9 +2,7 @@ // dispatch entry + list state, constructed and held by ClientSessions (one per browser client). // List data never enters zustand; React connects via subscribe/getListSnapshot. -import type { - IApiClient, SubagentAddress, SubagentCatalog, -} from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress, SubagentCatalog } from '@deepseek-ai/dsh-subagent/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types' import type { @@ -145,11 +143,10 @@ export class SessionManager { }) /** - * @param api - shared wire client. + * @param remote - generated Remote namespaces the Session cluster calls. * @param restoredSelection - persisted real-Session selection candidate. */ constructor( - private readonly api: IApiClient, private readonly remote: SessionRemotes, restoredSelection?: SessionId, restoredAddress?: SubagentAddress, @@ -321,7 +318,7 @@ export class SessionManager { const parentAvailable = address === undefined ? undefined : this.catalogs.get(address.parentSessionId)?.parentAvailable - return new Session(sessionId, this.api, this.remote, { + return new Session(sessionId, this.remote, { ...(address === undefined ? {} : { address, ...catalogAvailability(parentAvailable), @@ -369,7 +366,7 @@ export class SessionManager { this.notifier.markDirty() const operation = (async () => { try { - const { result } = await this.api.subagents.list({ parentSessionId }) + const result = toSessionResult(await this.remote.subagents.list(parentSessionId)) if (result.ok) { const parentAvailable = this.catalogInflight.get(parentSessionId)?.parentAvailableOverride ?? result.value.parentAvailable diff --git a/packages/api/session-controller/src/client/sessions/remotes.ts b/packages/api/session-controller/src/client/sessions/remotes.ts index 1449cc2c83..bec8709b90 100644 --- a/packages/api/session-controller/src/client/sessions/remotes.ts +++ b/packages/api/session-controller/src/client/sessions/remotes.ts @@ -8,6 +8,9 @@ import type { EncodedImageAttachment } from '@deepseek-ai/dsh-attachment/types' import type { ClientRemote } from '@deepseek-ai/dsh-api-gateway/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' +import type { + SubagentCatalog, SubagentInterruptReceipt, SubagentPromptReceipt, SubagentPromptRequest, +} from '@deepseek-ai/dsh-subagent/client' import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol' import type { SessionRemote } from '../transport.ts' @@ -21,9 +24,24 @@ export interface SessionCommandsRemote { ): Promise> } +/** Narrow subagent namespace consumed by a Client Session and its manager. */ +export interface SessionSubagentsRemote { + list(parentSessionId: SessionId, signal?: AbortSignal): Promise> + prompt( + request: SubagentPromptRequest, + signal?: AbortSignal, + ): Promise> + interruptByParent( + childSessionId: SessionId, + parentSessionId: SessionId, + mode: 'continuable', + ): Promise> +} + /** Generated Remote namespaces consumed by the Client Session object layer. */ export interface SessionRemotes { readonly $stream: ClientRemote['$stream'] readonly commands: SessionCommandsRemote readonly session: SessionRemote + readonly subagents: SessionSubagentsRemote } diff --git a/packages/api/session-controller/src/client/sessions/service.ts b/packages/api/session-controller/src/client/sessions/service.ts index 1dc484c615..a54d1ece0d 100644 --- a/packages/api/session-controller/src/client/sessions/service.ts +++ b/packages/api/session-controller/src/client/sessions/service.ts @@ -15,9 +15,7 @@ * survives frozen (read-only view) until the stage moves on. */ import type { Context, Fiber } from '@deepseek-ai/cordis' -import type { - IApiClient, SubagentAddress, -} from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { workspaceTitleOf } from '@deepseek-ai/dsh-util-workspace-path' import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types' @@ -218,12 +216,10 @@ export class ClientSessions implements ISessions { /** * @param ctx - client root context (scope fibers mount under it). - * @param api - wire client shared with every Session. * @param remote - generated Remote namespaces shared with every Session. */ constructor( private readonly rootCtx: Context, - api: IApiClient, remote: SessionRemotes, ) { this.selection = createSnapshotStore( @@ -231,7 +227,6 @@ export class ClientSessions implements ISessions { { persist: { name: 'dsh.sessions.current' } }) const restored = this.selection.getSnapshot() this.manager = new SessionManager( - api, remote, restored.sessionId, restored.subagentAddress, diff --git a/packages/api/session-controller/src/client/sessions/session.ts b/packages/api/session-controller/src/client/sessions/session.ts index 182d8514d9..db7cf9a997 100644 --- a/packages/api/session-controller/src/client/sessions/session.ts +++ b/packages/api/session-controller/src/client/sessions/session.ts @@ -3,9 +3,7 @@ import type { Context } from '@deepseek-ai/cordis' import { randomUUID } from '@deepseek-ai/dsh-util-crypto' import type { AttachmentIdType, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment' -import type { - IApiClient, SubagentAddress, -} from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { MessageId } from '@deepseek-ai/dsh-llm/brand' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { @@ -133,13 +131,11 @@ export class Session implements SessionFace { /** * @param sessionId - Host session identity (client sessions are always Host-born). - * @param api - shared wire client. * @param remote - generated Remote namespaces this session calls. * @param options - optional manager-owned state observers. */ constructor( readonly sessionId: SessionId, - private readonly api: IApiClient, private readonly remote: SessionRemotes, private readonly options: SessionOptions = {}, ) { @@ -222,13 +218,16 @@ export class Session implements SessionFace { }, } } else { - const routed = (await this.api.subagents.prompt({ - ...this.address, + const routed = toSessionResult(await this.remote.subagents.prompt({ + requestId: randomUUID() as SessionRequestId, + parentSessionId: this.address.parentSessionId, + childSessionId: this.address.childSessionId, + mode: this.address.mode, content: content.flatMap(part => part.type === 'text' ? [{ type: 'text' as const, text: part.text }] : []), clientTimeZone: resolvedClientTimeZone(), - }, signal)).result + }, signal)) result = routed.ok ? { ok: true, value: { accepted: true } } : routed } } @@ -290,7 +289,7 @@ export class Session implements SessionFace { /** * Stop the active turn while the Host preserves pending inbox work; failures * land in promptError (same error-strip display slot). A continuable - * subagent address routes through `subagent.interrupt`, whose durable + * subagent address routes through `subagents.interruptByParent`, whose durable * parent-address authority works without a live parent Agent; a one-shot * address stays uncancellable (the UI offers no stop action, so this arm is * defensive). @@ -314,7 +313,11 @@ export class Session implements SessionFace { let result: ClientResult<{ accepted: true }> try { result = address !== undefined - ? (await this.api.subagents.interrupt(address)).result + ? toSessionResult(await this.remote.subagents.interruptByParent( + address.childSessionId, + address.parentSessionId, + address.mode, + )) : toSessionResult(await this.remote.session.cancel({ sessionId: this.sessionId })) } catch (error) { result = transportResult(error) diff --git a/packages/api/session-controller/tsconfig.client.json b/packages/api/session-controller/tsconfig.client.json index 648055bcee..219893c980 100644 --- a/packages/api/session-controller/tsconfig.client.json +++ b/packages/api/session-controller/tsconfig.client.json @@ -21,6 +21,7 @@ { "path": "../../llm/llm" }, { "path": "../../session/session-projection" }, { "path": "../../session/session-title" }, + { "path": "../../subagent/subagent" }, { "path": "../../util/brand" }, { "path": "../../util/crypto" }, { "path": "../../util/workspace-path" }, diff --git a/packages/client/connection/src/client/api.ts b/packages/client/connection/src/client/api.ts index a0ea093533..3a3c78dfa5 100644 --- a/packages/client/connection/src/client/api.ts +++ b/packages/client/connection/src/client/api.ts @@ -14,7 +14,6 @@ export type { ModelReasoningEffort, ModelSelection, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, - SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, } from '@deepseek-ai/dsh-host-apiproxy/api' export type { RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, diff --git a/packages/client/connection/src/client/index.ts b/packages/client/connection/src/client/index.ts index d53c48d019..358b2f41cc 100644 --- a/packages/client/connection/src/client/index.ts +++ b/packages/client/connection/src/client/index.ts @@ -35,7 +35,6 @@ export type { SkillsApi, SkillEntry, ModelCatalog, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, MessageId, ModelReasoningEffort, ModelSelection, - SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, ClientRequest, ServerResponse, RpcMessage, HostDescription, IApiClient, SessionId, SessionEvent, ContentBlock, StreamChunk, diff --git a/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx b/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx index 308a713b9f..6fa3da23a1 100644 --- a/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx +++ b/packages/client/ui-subagent/src/client/SubagentHeaderLineage.tsx @@ -6,7 +6,7 @@ import { type SessionListState, type SessionProjectionMap, type SessionSummary, type SubagentCatalogSnapshot, } from '@deepseek-ai/dsh-api-session-controller/client' -import type { SubagentAddress } from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { IconChevronDownOutline14, IconChevronRightOutline14, IconRefreshOutline14, StateDot, @@ -14,7 +14,6 @@ import { import type { PropsLocale, PropsRuntime, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots' import { NS } from './locales.ts' import type {} from '@deepseek-ai/dsh-client-ui-conversation/client' -import type {} from '@deepseek-ai/dsh-subagent/client' import type {} from '@deepseek-ai/dsh-token-meter/client' import css from './SubagentHeaderLineage.module.css' import { indexSubagentDescendants } from './subagent-lineage.ts' diff --git a/packages/client/ui-subagent/src/client/index.ts b/packages/client/ui-subagent/src/client/index.ts index 452123427c..a5a6f4f481 100644 --- a/packages/client/ui-subagent/src/client/index.ts +++ b/packages/client/ui-subagent/src/client/index.ts @@ -1,6 +1,6 @@ /** Web subagent catalog, navigation, and addressed-session composer owner. */ import type { Context as ClientContext } from '@deepseek-ai/cordis' -import type { SubagentAddress } from '@deepseek-ai/dsh-client-connection/client' +import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { ComposerChainProps } from '@deepseek-ai/dsh-client-ui-conversation/client' import { SubagentHeaderLineage, type SubagentCatalogInjected } from './SubagentHeaderLineage.tsx'