mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-12 04:01:20 +00:00
refactor(api): converge the Remote failure vocabulary and client surface
Single RemoteError with a merge-extensible, domain-prefixed code map; owners throw at the failure point; streams surface marked failures; clients consume ctx.remote directly with isRemoteFailure as the only discrimination point and construct no failure instances.
This commit is contained in:
@@ -6,12 +6,14 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { z } from 'zod'
|
||||
import { DirectoryPickerError } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import type { DirectoryPickerCapabilities } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import type {
|
||||
DirectoryPickerCapabilities, DirectoryPickerErrorCode,
|
||||
} from '@deepseek-ai/dsh-host-directory-picker'
|
||||
// The seam owns the listing declaration; the generator requires the reference
|
||||
// site to name that package rather than this package's re-export of it.
|
||||
import type { DirectoryListing } from '@deepseek-ai/dsh-host-directory-picker/types'
|
||||
import { Remote, TypertRemoteFailure, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import type { DirectoryPickerErrorDetailsMap } from './types.ts'
|
||||
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import type { RemoteErrorCode } from '@deepseek-ai/dsh-typert-protocol'
|
||||
|
||||
const createDirectoryRequestSchema = z.object({
|
||||
path: z.string(),
|
||||
@@ -86,8 +88,8 @@ export class DirectoryPickerController extends TypertRemoteService {
|
||||
async createDirectory(path: string, name: string): Promise<string> {
|
||||
const request = createDirectoryRequestSchema.safeParse({ path, name })
|
||||
if (!request.success) {
|
||||
throw pickerFailureOf(
|
||||
'bad-request',
|
||||
throw new RemoteError(
|
||||
'gateway/bad-request',
|
||||
'invalid payload for host.createDirectory',
|
||||
{ issues: request.error.issues },
|
||||
)
|
||||
@@ -107,8 +109,8 @@ export class DirectoryPickerController extends TypertRemoteService {
|
||||
): DirectoryPickerCapabilities[Kind] {
|
||||
const capability = this.ctx.directoryPicker.capability()
|
||||
if (capability.kind !== kind) {
|
||||
throw pickerFailureOf(
|
||||
'directory-picker-unavailable',
|
||||
throw new RemoteError(
|
||||
'directory-picker/unavailable',
|
||||
`directoryPicker.${method} needs the ${kind} capability; the composed picker serves "${capability.kind}"`,
|
||||
{ capability: capability.kind },
|
||||
)
|
||||
@@ -118,19 +120,15 @@ export class DirectoryPickerController extends TypertRemoteService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Raise one entry of the picking wire failure vocabulary.
|
||||
* @param code - the failure code a caller discriminates on.
|
||||
* @param message - operator-facing description.
|
||||
* @param details - the payload this code carries.
|
||||
* @returns the failure to throw across the Remote boundary.
|
||||
* Wire code answered for each seam browse failure. The seam's closed codes are
|
||||
* its own local vocabulary, so this controller owns the projection onto the
|
||||
* `directory-picker/*` codes a Remote caller discriminates on.
|
||||
*/
|
||||
function pickerFailureOf<Code extends keyof DirectoryPickerErrorDetailsMap>(
|
||||
code: Code,
|
||||
message: string,
|
||||
details: DirectoryPickerErrorDetailsMap[Code],
|
||||
): TypertRemoteFailure {
|
||||
return new TypertRemoteFailure({ code, message, details })
|
||||
}
|
||||
const BROWSE_FAILURE_CODES = {
|
||||
'directory-unreadable': 'directory-picker/unreadable',
|
||||
'directory-exists': 'directory-picker/exists',
|
||||
'directory-create-failed': 'directory-picker/create-failed',
|
||||
} as const satisfies Record<DirectoryPickerErrorCode, RemoteErrorCode>
|
||||
|
||||
/**
|
||||
* Classify a browse-primitive rejection: the seam's own closed codes carry the
|
||||
@@ -138,16 +136,21 @@ function pickerFailureOf<Code extends keyof DirectoryPickerErrorDetailsMap>(
|
||||
* @param error - the primitive's rejection.
|
||||
* @returns the failure to throw across the Remote boundary.
|
||||
*/
|
||||
function browseFailure(error: unknown): TypertRemoteFailure {
|
||||
function browseFailure(error: unknown): RemoteError {
|
||||
if (error instanceof DirectoryPickerError) {
|
||||
return pickerFailureOf(error.code, error.message, { path: error.path })
|
||||
return new RemoteError(
|
||||
BROWSE_FAILURE_CODES[error.code],
|
||||
error.message,
|
||||
{ path: error.path },
|
||||
{ cause: error },
|
||||
)
|
||||
}
|
||||
return pickerFailureOf('internal', errorMessage(error), {})
|
||||
return new RemoteError('gateway/internal', errorMessage(error), {}, { cause: error })
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a cancellable primitive's rejection. An abort is the caller's own
|
||||
* timeout or disconnect, not a backend failure, so it answers `cancelled`
|
||||
* timeout or disconnect, not a backend failure, so it answers `gateway/cancelled`
|
||||
* before the business classification runs.
|
||||
* @param error - the primitive's rejection.
|
||||
* @param signal - the caller lifetime the primitive ran under.
|
||||
@@ -160,10 +163,10 @@ function cancellableFailure(
|
||||
signal: AbortSignal,
|
||||
cancelled: string,
|
||||
failed?: string,
|
||||
): TypertRemoteFailure {
|
||||
if (signal.aborted) return pickerFailureOf('cancelled', cancelled, {})
|
||||
): RemoteError {
|
||||
if (signal.aborted) return new RemoteError('gateway/cancelled', cancelled, {}, { cause: error })
|
||||
if (failed === undefined) return browseFailure(error)
|
||||
return pickerFailureOf('internal', `${failed}: ${errorMessage(error)}`, {})
|
||||
return new RemoteError('gateway/internal', `${failed}: ${errorMessage(error)}`, {}, { cause: error })
|
||||
}
|
||||
|
||||
function errorMessage(error: unknown): string {
|
||||
|
||||
Reference in New Issue
Block a user