refactor(host)!: retire the skill.invoke RPC for the gesture boundary
Invocation is an ordinary session.prompt again: the pre-step gesture boundary makes it deterministic host-side for every front end, so the dedicated RPC (handler, wire schema, error codes, client face, fixtures) and ui-skill's claim machinery are net deletions. The menu keeps decision 21 exactly — a pick lands literal /name text — plus the user-only marker from skill.list's modelInvocable flag.
This commit is contained in:
@@ -24,7 +24,6 @@
|
||||
import {
|
||||
memo, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode,
|
||||
} from 'react'
|
||||
import { opensUserTurn } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {
|
||||
CodeSubCall, CommandNode, ConversationNode, ConversationSnapshot, RunningToolCall, ToolResultNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
@@ -119,7 +118,7 @@ function activeRetrySeq(nodes: readonly ConversationNode[], running: boolean): n
|
||||
const node = nodes[index]
|
||||
if (node === undefined) continue
|
||||
if (node.kind === 'model-retry') return node.retryState === 'cancelled' ? null : node.seq
|
||||
if (node.kind === 'assistant' || opensUserTurn(node)) return null
|
||||
if (node.kind === 'assistant' || node.kind === 'user') return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -448,11 +447,10 @@ export function ChatView({
|
||||
return
|
||||
}
|
||||
firstSeqRef.current = firstSeq
|
||||
// Own words must be visible: a new trailing user-turn node (a prompt or an
|
||||
// explicit skill invocation) force-scrolls (send lives in the composer, so
|
||||
// arrival is detected here, not armed there).
|
||||
// Own words must be visible: a new trailing user node force-scrolls
|
||||
// (send lives in the composer, so arrival is detected here, not armed there).
|
||||
const appendedUser = lastKey !== lastKeyRef.current
|
||||
&& lastItem !== undefined && lastItem.kind === 'node' && opensUserTurn(lastItem.node)
|
||||
&& lastItem !== undefined && lastItem.kind === 'node' && lastItem.node.kind === 'user'
|
||||
const appendedSteering = lastSteeringId !== null && lastSteeringId !== lastSteeringIdRef.current
|
||||
const tipMoved = followSigRef.current !== followSig
|
||||
lastKeyRef.current = lastKey
|
||||
|
||||
@@ -256,30 +256,3 @@
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* User-explicit skill invocation: the injected body collapses behind a
|
||||
disclosure inside the user bubble. */
|
||||
.skillInvocationDetails {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.skillInvocationSummary {
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
color: var(--dsw-alias-label-secondary);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.skillInvocationBody {
|
||||
margin: 6px 0 0;
|
||||
padding: 8px;
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
border-radius: 6px;
|
||||
background: var(--dsw-alias-bg-secondary, rgba(0, 0, 0, 0.06));
|
||||
font-family: var(--dsw-font-mono, monospace);
|
||||
font-size: 0.78em;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
import { memo, useEffect, useMemo, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import type {
|
||||
CompactionSummaryNode, ContextMessageNode, ModelRetryNode, SkillInvocationNode,
|
||||
SteeringMessageNode, TurnErrorNode, UnknownSurfaceNode, UserMessageNode,
|
||||
CompactionSummaryNode, ContextMessageNode, ModelRetryNode, SteeringMessageNode,
|
||||
TurnErrorNode, UnknownSurfaceNode, UserMessageNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { JsonBlock, MessageText, StateDot } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ChatViewSlotProps } from '../contract/slots.ts'
|
||||
@@ -22,7 +22,6 @@ export interface MessageItemProps {
|
||||
| UserMessageNode
|
||||
| SteeringMessageNode
|
||||
| ContextMessageNode
|
||||
| SkillInvocationNode
|
||||
| CompactionSummaryNode
|
||||
| ModelRetryNode
|
||||
| TurnErrorNode
|
||||
@@ -192,38 +191,6 @@ function UserStyleBubble({
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-explicit skill invocation: the right-aligned bubble presents the
|
||||
* `/name args` gesture from source metadata (never re-parsed from the body),
|
||||
* and the injected `<skill_content>` collapses behind a disclosure — the
|
||||
* durable content is model-facing bulk, not conversation prose.
|
||||
*/
|
||||
function SkillInvocationRow({ node, t }: {
|
||||
node: SkillInvocationNode
|
||||
t: ChatViewSlotProps['t']
|
||||
}): ReactNode {
|
||||
const { text } = contentText(node.content)
|
||||
return (
|
||||
<div className={css.userRow} data-skill-invocation data-time-hover-root>
|
||||
<div className={css.bubble}>
|
||||
<span className={css.refChip} data-ref-chip="skill">{`/${node.name}`}</span>
|
||||
{node.args !== undefined && <MessageText text={` ${node.args}`} />}
|
||||
<details className={css.skillInvocationDetails}>
|
||||
<summary className={css.skillInvocationSummary}>{t('message.skillInvocation.expand')}</summary>
|
||||
<pre className={css.skillInvocationBody}>{text}</pre>
|
||||
</details>
|
||||
</div>
|
||||
<MessageIconActions
|
||||
text={text}
|
||||
time={node.time}
|
||||
clock="start"
|
||||
className={css.actions}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Render one Host-authoritative pending steering item with the same visual
|
||||
* language as its eventual durable transcript node.
|
||||
@@ -285,8 +252,6 @@ export const MessageItem = memo(function MessageItem({
|
||||
t={t}
|
||||
/>
|
||||
)
|
||||
case 'skill-invocation':
|
||||
return <SkillInvocationRow node={node} t={t} />
|
||||
case 'compaction':
|
||||
return <CompactionItem node={node} t={t} />
|
||||
case 'model-retry':
|
||||
|
||||
@@ -79,7 +79,6 @@ export const zh = {
|
||||
'message.context.recall.counts': '保留 {retained} 条 · 省略 {omitted} 条',
|
||||
'message.context.recall.truncated': '已截断',
|
||||
'message.steering': '插话',
|
||||
'message.skillInvocation.expand': '查看注入的 skill 内容',
|
||||
'message.compaction': '上下文已压缩',
|
||||
'message.compaction.expand': '点击查看压缩摘要',
|
||||
'message.compaction.unavailable': '压缩摘要不可用',
|
||||
@@ -220,7 +219,6 @@ export const en = {
|
||||
'message.context.recall.counts': '{retained} kept · {omitted} omitted',
|
||||
'message.context.recall.truncated': 'truncated',
|
||||
'message.steering': 'Interjection',
|
||||
'message.skillInvocation.expand': 'View injected skill content',
|
||||
'message.compaction': 'Context compacted',
|
||||
'message.compaction.expand': 'View compaction summary',
|
||||
'message.compaction.unavailable': 'Compaction summary unavailable',
|
||||
|
||||
Reference in New Issue
Block a user