mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
refactor(api-session-controller): route subagent calls through Remote
This commit is contained in:
@@ -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<T> =
|
||||
@@ -13,7 +14,7 @@ export type ClientResult<T> =
|
||||
|
||||
/**
|
||||
* 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<T>(error: unknown): ClientResult<T> {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<RemoteResult<object | undefined>>
|
||||
}
|
||||
|
||||
/** Narrow subagent namespace consumed by a Client Session and its manager. */
|
||||
export interface SessionSubagentsRemote {
|
||||
list(parentSessionId: SessionId, signal?: AbortSignal): Promise<RemoteResult<SubagentCatalog>>
|
||||
prompt(
|
||||
request: SubagentPromptRequest,
|
||||
signal?: AbortSignal,
|
||||
): Promise<RemoteResult<SubagentPromptReceipt>>
|
||||
interruptByParent(
|
||||
childSessionId: SessionId,
|
||||
parentSessionId: SessionId,
|
||||
mode: 'continuable',
|
||||
): Promise<RemoteResult<SubagentInterruptReceipt>>
|
||||
}
|
||||
|
||||
/** 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
|
||||
}
|
||||
|
||||
@@ -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<SessionSelection>(
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user