mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
Merge remote-tracking branch 'origin/master' into codex/pr-3111-review
# Conflicts: # packages/api/session-controller/src/client/contract/snapshot.ts # packages/test-support/client-runtime/src/sessions.ts
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 { SessionRequestId } from '../../types.ts'
|
||||
import type { ClientFailure } from './result.ts'
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ export const inject = [
|
||||
'remote',
|
||||
'remote.commands',
|
||||
'remote.session',
|
||||
'remote.subagents',
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,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 {
|
||||
@@ -143,13 +141,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 = {},
|
||||
) {
|
||||
@@ -258,13 +254,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
|
||||
}
|
||||
}
|
||||
@@ -327,7 +326,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).
|
||||
@@ -351,7 +350,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)
|
||||
|
||||
Reference in New Issue
Block a user