refactor: brand the command lifecycle pairing id as CommandId
commandId crosses three boundaries (session log, wire admission response, client flow pairing), so per the branded-id rule it becomes Branded<'CommandId'>, declared in a new pure @deepseek-ai/dsh-commands/brand outlet (the dsh-llm/brand shape: type + constructor, no Context merges, so wire and client programs can name it without loading the host plugin). The event payloads, CommandExecution, and the executor mint carry the brand; the wire schema gains commandIdSchema as the domain's single brand-cast point (the approvals precedent); CommandNode and the fixture's fabrication cast follow type-only.
This commit is contained in:
@@ -15,12 +15,17 @@
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./brand": {
|
||||
"types": "./lib/types/brand.d.ts",
|
||||
"default": "./lib/types/brand.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/types/**/*.js",
|
||||
"lib/types/**/*.d.ts",
|
||||
"lib/types/**/*.d.ts.map",
|
||||
"src"
|
||||
@@ -28,6 +33,7 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-brand": "^0.0.1",
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"@deepseek-ai/dsh-scope": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
@@ -35,6 +41,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-brand": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-scope": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
|
||||
29
packages/ui/commands/src/brand.ts
Normal file
29
packages/ui/commands/src/brand.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* dsh-commands' owned branded id: command lifecycle pairing across the
|
||||
* session log, the wire admission response, and client-side flow pairing.
|
||||
*
|
||||
* The `Branded<B>` primitive lives in `@deepseek-ai/dsh-brand`; this module
|
||||
* is a pure type/constructor outlet (no cordis imports, no module
|
||||
* augmentation) so wire and client programs can name the brand without
|
||||
* loading the host plugin's Context merges — the `dsh-llm/brand` shape.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-commands/brand
|
||||
*/
|
||||
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
|
||||
/**
|
||||
* Pairs one command execution's `command/run`/`command/done` lifecycle
|
||||
* records with each other and with the `command.execute` admission response.
|
||||
* Minted by the executor, monotonic per service instance.
|
||||
*/
|
||||
export type CommandId = Branded<'CommandId'>
|
||||
|
||||
/**
|
||||
* Brand a string as a {@link CommandId}.
|
||||
* @param id - the executor-minted pairing id.
|
||||
* @returns the same string, branded; no validation is performed.
|
||||
*/
|
||||
export function CommandId(id: string): CommandId {
|
||||
return id as CommandId
|
||||
}
|
||||
@@ -8,6 +8,9 @@ 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 { CommandId } from './brand.ts'
|
||||
|
||||
export { CommandId } from './brand.ts'
|
||||
|
||||
export const name = 'commands'
|
||||
|
||||
@@ -55,7 +58,7 @@ export type CommandResult =
|
||||
*/
|
||||
export interface CommandExecution {
|
||||
/** Pairing id carried by this execution's lifecycle events. */
|
||||
readonly commandId: string
|
||||
readonly commandId: CommandId
|
||||
/** The handler's normalized outcome. */
|
||||
readonly result: CommandResult
|
||||
}
|
||||
@@ -131,13 +134,13 @@ declare module '@deepseek-ai/dsh-session' {
|
||||
* folding its own command records, a rich command card) never re-parses
|
||||
* a line.
|
||||
*/
|
||||
'command/run': { commandId: string; name: string; args: string; source: CommandSource }
|
||||
'command/run': { commandId: CommandId; name: string; args: string; source: CommandSource }
|
||||
/**
|
||||
* The paired command settled. `kind`/`text` carry the handler's verbatim
|
||||
* outcome (a thrown/aborted handler settles as `kind: 'error'` with the
|
||||
* rendered failure); presentation stays client-computed at render time.
|
||||
*/
|
||||
'command/done': { commandId: string; kind: 'success' | 'error'; text?: string }
|
||||
'command/done': { commandId: CommandId; kind: 'success' | 'error'; text?: string }
|
||||
}
|
||||
|
||||
interface OutOfBandSessionEventMap {
|
||||
@@ -397,9 +400,9 @@ export class CommandService extends Service {
|
||||
}
|
||||
|
||||
/** Mint the next pairing id (monotonic; instance-token-prefixed so a resumed log never repeats one). */
|
||||
private mintCommandId(): string {
|
||||
private mintCommandId(): CommandId {
|
||||
this.commandSeq += 1
|
||||
return `cmd-${this.instanceToken}-${this.commandSeq}`
|
||||
return CommandId(`cmd-${this.instanceToken}-${this.commandSeq}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
{
|
||||
"path": "../../core/session"
|
||||
},
|
||||
{
|
||||
"path": "../../util/brand"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user