Merge remote-tracking branch 'origin/master' into feat/web-message-feedback-ui

Adapt to two contract changes master introduced:

- The generated Remote face now wraps every business result in
  RemoteResult, folding carrier failures into an ok:false branch instead
  of rejecting. The controller reads that envelope at its three call
  sites and maps a carrier failure onto the same settled shape the
  controls already render; three specs cover the new branch.
- Client packages split their tsconfig into host and client halves, and
  the host aggregate now compiles any test not named *.client.spec.*.
  Rename this package's specs to the client convention and drop the
  ../connection project reference, which pointed at a solution file that
  no longer carries the client sources.

Keep master's mount loop with its rollback-on-failure in api-remotes and
add messageFeedbackRemote to it.
This commit is contained in:
Chinesezjc
2026-08-12 10:43:23 +08:00
parent 47f254a252
commit b462d5fd69
507 changed files with 3130 additions and 2238 deletions
+12 -42
View File
@@ -3,26 +3,27 @@
* @module @deepseek-ai/dsh-commands
*/
import { Context, Service } from '@deepseek-ai/cordis'
import { Context } from '@deepseek-ai/cordis'
import type { Agent } from '@deepseek-ai/dsh-agent'
import { NamedEntries, ScopedLayers } from '@deepseek-ai/dsh-scope'
import type { ScopeKey, ScopeLayer } from '@deepseek-ai/dsh-scope'
import type { Session, SessionEvent, SessionEventMap } from '@deepseek-ai/dsh-session'
import { GatewayService, Remote } from '@deepseek-ai/dsh-type-meta'
import { CommandId } from './brand.ts'
import type {
CommandDescriptor,
CommandExecution,
CommandInputDescriptor,
CommandResult,
} from './types.ts'
export { CommandId } from './brand.ts'
export type { CommandSource, CommandSourceMap } from './types.ts'
export type * from './types.ts'
export const name = 'commands'
const COMMAND_NAME = /^[a-z][a-z0-9_-]*$/u
/** Immutable metadata for a command's optional unstructured input. */
export interface CommandInputDescriptor {
/** Placeholder shown before the user supplies free-form input. */
readonly hint: string
}
/** Invocation passed to one registered command handler. */
export interface CommandInvocation {
/** Pairing id already written to this invocation's `command/run` event. */
@@ -35,29 +36,6 @@ export interface CommandInvocation {
readonly signal: AbortSignal
}
/** Expected command outcome rendered directly by the dispatching UI. */
export type CommandResult =
| {
readonly kind: 'success'
readonly text?: string
/** Earlier authoritative domain event that owns a richer presentation. */
readonly sourceEventSeq?: number
}
| { readonly kind: 'error'; readonly text: string }
/**
* One settled command execution: the handler's normalized result plus the
* lifecycle pairing id minted for its `command/run`/`command/done` records,
* so a dispatching surface can correlate the RPC-level acknowledgment with
* the flow node those events produce.
*/
export interface CommandExecution {
/** Pairing id carried by this execution's lifecycle events. */
readonly commandId: CommandId
/** The handler's normalized outcome. */
readonly result: CommandResult
}
/** Plugin-owned command registration. */
export interface CommandDefinition {
/** Lowercase command name without the leading slash. */
@@ -76,16 +54,6 @@ export interface CommandDefinition {
readonly handler: (invocation: CommandInvocation) => CommandResult | Promise<CommandResult>
}
/** Handler-free immutable command view returned to UI adapters. */
export interface CommandDescriptor {
/** Lowercase command name without the leading slash. */
readonly name: string
/** Human-readable summary used in discovery UI. */
readonly description: string
/** Optional free-form input hint advertised to capable clients. */
readonly input?: CommandInputDescriptor
}
/** Syntactically valid slash command before registry resolution. */
export interface ParsedCommand {
/** Lowercase command name without the leading slash. */
@@ -254,7 +222,7 @@ function normalizeResult(command: string, value: unknown): CommandResult {
* registered through a command-injected child of an agent context shadow
* globals for that agent.
*/
export class CommandService extends Service {
export class CommandService extends GatewayService {
private readonly layers = new ScopedLayers(
scope => new CommandLayer(scope),
() => { this.notifyChange() },
@@ -288,6 +256,7 @@ export class CommandService extends Service {
* @param agent - exact receiving agent and scoped-layer key.
* @returns name-sorted descriptors after scoped shadowing.
*/
@Remote
list(agent: Agent): readonly CommandDescriptor[] {
return Object.freeze([...this.view(agent).values()]
.map(command => command.descriptor)
@@ -324,6 +293,7 @@ export class CommandService extends Service {
* @returns the settled execution (result + lifecycle pairing id), or
* `undefined` when syntax or name does not resolve.
*/
@Remote
async execute(
agent: Agent,
line: string,