Files
deepseek-harness/packages/client/ui-conversation/src/client/conversation-nodes/message.ts
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.

The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:13 +08:00

84 lines
2.9 KiB
TypeScript

import type { Context } from '@deepseek-ai/cordis'
import type {
ContextMessageNode, ConversationNodeDefinition, SteeringMessageNode, UserMessageNode,
} from '@deepseek-ai/dsh-client-runtime/client'
import {
contextForm, contextProvenance, isAppendSurfaceEvent, isReplacementSurfaceEvent,
} from '@deepseek-ai/dsh-client-runtime/client'
import type { InboxState } from './inbox.ts'
import { chatNode } from './common.ts'
type MessageNode = UserMessageNode | SteeringMessageNode | ContextMessageNode
declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
interface ChatNodeDataMap {
/** Ordinary turn-opening user message. */
user: UserMessageNode
/** User message admitted into an active turn. */
steering: SteeringMessageNode
/** Non-user context injected into model history. */
context: ContextMessageNode
}
}
function isCompactionCheckpoint(event: Parameters<ConversationNodeDefinition['match']>[0]): boolean {
if (event.type !== 'user/message' || !isReplacementSurfaceEvent(event)) return false
const source = event.data.source
return source.kind === 'plugin' && source.plugin === 'compact'
}
/** User, steering, and injected-context message classification Definition. */
export const messageDefinition: ConversationNodeDefinition<MessageNode> = {
kind: 'input-message',
match: event => event.type === 'user/message'
&& isAppendSurfaceEvent(event)
&& !isCompactionCheckpoint(event)
? { id: String(event.data.id), role: 'start' }
: null,
start: (_context, match, reader) => {
if (match.event.type !== 'user/message') throw new Error('input-message start requires user/message')
const event = match.event
if (event.data.source.kind !== 'user') {
return {
kind: 'context',
seq: event.seq,
time: event.time,
content: event.data.content,
source: event.data.source,
provenance: contextProvenance(event.data.source),
form: contextForm(event.data.source),
}
}
const claimed = reader.previous<InboxState>('inbox-next-step')?.state.claimed.has(String(event.data.id)) === true
return claimed
? {
kind: 'steering',
messageId: event.data.id,
seq: event.seq,
time: event.time,
content: event.data.content,
source: event.data.source,
}
: {
kind: 'user',
seq: event.seq,
time: event.time,
content: event.data.content,
source: event.data.source,
}
},
update: context => context.state,
buildViewNode: (context, target) => {
if (target !== 'chat' || context.state === undefined) return null
return chatNode(context, context.state.kind, context.state.seq, context.state)
},
}
/**
* Register the user, steering, and injected-context message contribution.
* @param ctx - owning UI Conversation context.
*/
export function registerMessageConversationNode(ctx: Context): void {
ctx.conversationEvents.register(messageDefinition)
}