fix(web): guide clickable file references
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/ui-deliverables/README.md
|
||||
README.md: 9cfcde9510c5d6814d5b42fd58a0520ea37b8679
|
||||
README.zh.md: 4e09524e269df31cdabce0d5f6e6900d23305265
|
||||
README.md: e7118eac75f31b3ffc3f2434371dba06f029d241
|
||||
README.zh.md: 0c41170ef38331166f6ad87df0c3e5976200eaf9
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Produced-files feature owner: registers the deliverables row a finished turn ends with into the chat view's `conversation.chat.turnTail` hole. All policy lives here; removing this plugin's line from cordis.yml removes the surface entirely, and the owning view renders an empty hole at zero cost.
|
||||
Produced-files and clickable-reference feature owner. The Node half registers final-response guidance with the system-prompt registry; the browser half registers the deliverables row a finished turn ends with into the chat view's `conversation.chat.turnTail` hole and links matching inline-code references in the closing prose. The shipped Web patch is the only composition that loads this package. Removing its one cordis.yml entry removes the guidance, row, and prose links together.
|
||||
|
||||
`deliverablesDefinition` folds each Turn's successful mutation calls into engine-published `DeliverablesTurnData`; `producedForClosing` reads that data with the closing Assistant seq. The vocabulary is the mutation tools' own follow-along `locations`, never the closing prose: a produced file is listed whether or not the model remembered to name it. A mutation is recognized by render intent, not tool name — a diff card, or a generic card whose `kind` is `edit` (the shape `str_replace_editor`'s insert presents) — so a new mutation tool joins by declaring what it does. Reads, deletes, and failed calls contribute nothing; a path appears once per Turn in first-seen order. The Conversation Location index owns Turn membership, so a Turn that mutates and then ends without content text cannot spill into the next Turn's row.
|
||||
|
||||
@@ -10,15 +10,26 @@ Produced-files feature owner: registers the deliverables row a finished turn end
|
||||
|
||||
The closing prose carries the same vocabulary. This plugin provides the `chatFileMentions` service the chat view consults per closing message: `producedFileMentions` resolves an inline-code token by exact path, or by being exactly the basename of exactly one produced path — a basename two paths share stays inert rather than guessing, so a mention link can never open the wrong file or 404. A resolved mention keeps its code chip and takes the markdown sheet's link language — link-blue at rest, underlined on hover, exactly like URL-promoted inline code — with the full path as its `title`; mentions never render inside anchors or streaming text. Decision record: the [inline file mentions Agent Note](../../../.agents/notes/implemented/feature/2026-08-07-web-inline-file-mentions.md).
|
||||
|
||||
The Node half registers the static `ui:deliverable-file-references` system-prompt section. It asks the model to mention the primary files it successfully created or modified and to write those and any other changed-file references as Markdown inline code, using the exact file-tool path or a basename only when unique within the Turn. The guidance makes the renderer's accepted syntax explicit; it does not govern unrelated path discussions or widen the renderer's successful-mutation vocabulary.
|
||||
|
||||
## Model Experience
|
||||
|
||||
None, as the row is a pure client derivation over already-logged tool metadata and nothing here reaches a model request.
|
||||
### Clickable file-reference guidance
|
||||
|
||||
#### What the model sees
|
||||
|
||||
One fixed paragraph instructs the model to name primary files from successful creation or modification calls in its final response and to format those and any other changed-file references as exact-path or unique-basename Markdown inline code, such as `out/report.html`.
|
||||
|
||||
#### Token effect
|
||||
|
||||
One fixed prompt paragraph whenever this package is loaded; no tool schema, tool result, or per-Turn context is added.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
None; this package neither assembles nor sends provider requests.
|
||||
The section is static at order 190 for the lifetime of the package mount, so it remains in the reusable prompt prefix and does not change across Turns.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Mention matching is exact path or unique basename only.** A suffix mention (`out/index.html` written as `index.html` resolves; `deep/out/index.html` written as `out/index.html` does not) stays inert; widening the matcher is deferred until a real closing-message shape needs it.
|
||||
- **Files created indirectly by terminal commands remain outside the matching vocabulary.** Naming such a file in inline code does not make it clickable unless a successful mutation location also records that path.
|
||||
- **Native folder handoff targets the Host desktop.** A browser reached through a non-loopback authority omits the action, as does a deployment reporting no native opener. SSH forwarding that makes a remote Host look loopback-local must set the gateway's `nativeOpen: false`; so must a headless macOS/Windows Host, a WSL deployment without working Windows interop, or any Linux desktop whose display/opener probe is a false positive. Identifying the operator-visible desktop remains deployment policy.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
产出文件功能的属主:把已完成轮次末尾的产出文件行注册到 chat 视图的 `conversation.chat.turnTail` slot 中。全部策略都在本包内;从 cordis.yml 中删去本插件那一行即可整体移除该界面,属主视图无需额外开销即可渲染空 slot。
|
||||
产出文件与可点击文件引用功能的属主。Node 侧向系统提示词 registry 注册最终回复指引;浏览器侧把已完成轮次末尾的产出文件行注册到 chat 视图的 `conversation.chat.turnTail` slot,并将收尾正文中匹配的行内代码引用转换为链接。正式提供的组合中只有 Web patch 加载本包;从 cordis.yml 中删去这一项会同时移除提示词、文件行与正文链接。
|
||||
|
||||
`deliverablesDefinition` 把每个轮次中成功的修改调用折叠进引擎发布的 `DeliverablesTurnData`;`producedForClosing` 结合收尾 Assistant 的 seq 读取这份数据。依据的是修改工具自身附带的 `locations`,而不是收尾正文:无论模型是否记得点名,产出文件都会被列出。修改操作按渲染意图而非工具名识别:diff 卡片,或 `kind` 为 `edit` 的通用卡片(即 `str_replace_editor` 的 insert 操作所呈现的形态);因此新的修改工具只需声明自身行为即可加入。读取、删除和失败的调用不贡献任何条目;同一路径在一个轮次内按首见顺序只出现一次。Conversation Location 索引负责维护轮次归属关系,因此一个轮次即使先修改文件、随后没有正文内容就结束,也不会溢进下一个轮次的行里。
|
||||
|
||||
@@ -10,15 +10,26 @@
|
||||
|
||||
收尾正文承载同一份词表。本插件提供供 chat 视图按收尾消息查询的 `chatFileMentions` 服务:`producedFileMentions` 按精确路径解析行内代码 token,或当 token 恰好等于某条产出路径的 basename,且这样的路径仅有一条时解析——两条路径共享同一 basename 时,文本保持不可点击而不作猜测,因此提及链接永远不会打开错误的文件,也不会导致 404。解析成功的提及保留代码标签,并采用 Markdown 样式表的链接样式:静止时为链接蓝色,悬停时显示下划线,与 URL 提升的行内代码完全一致——完整路径作为其 `title`;提及绝不会渲染在链接内部或流式文本中。决策记录:[行内文件提及 Agent Note](../../../.agents/notes/implemented/feature/2026-08-07-web-inline-file-mentions.md)。
|
||||
|
||||
Node 侧注册静态系统提示词段落 `ui:deliverable-file-references`。它要求模型点名成功创建或修改的主要文件,并将这些文件以及正文中提到的其他本轮变更文件写成 Markdown 行内代码:使用文件工具采用的精确路径,或仅在 basename 能唯一指代本轮文件时使用 basename。该提示词只向模型说明渲染器接受的语法;它不约束无关的路径讨论,也不会扩大渲染器的成功修改词表。
|
||||
|
||||
## 模型体验
|
||||
|
||||
无。该行是对已记录工具元数据的纯客户端派生,这里没有任何内容进入模型请求。
|
||||
### 可点击文件引用指引
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
一段固定提示词要求模型在最终回复中点名成功创建或修改的主要文件,并将这些文件以及正文中提到的其他本轮变更文件写成采用精确路径或唯一 basename 的 Markdown 行内代码,例如 `out/report.html`。
|
||||
|
||||
#### Token 影响
|
||||
|
||||
加载本包时增加一段固定提示词;不增加工具 schema、工具结果或按 Turn 变化的上下文。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
无;该包既不组装也不发送提供方请求。
|
||||
该段落在本包加载期间始终以顺序 190 保持静态,因此留在可复用的提示词前缀中,不会随 Turn 改变。
|
||||
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **提及匹配只认精确路径或唯一 basename。**后缀式提及(`out/index.html` 写作 `index.html` 可解析;`deep/out/index.html` 写作 `out/index.html` 则不行)保持不可点击;等真实的收尾消息形态产生需求后再放宽匹配规则。
|
||||
- **终端命令间接创建的文件仍不在匹配词表内。**除非某个成功修改位置也记录了该路径,否则在行内代码中点名这类文件不会使其可点击。
|
||||
- **原生文件夹交接以 Host 桌面为目标。**经非 loopback 权威访问的浏览器会省略该操作,报告没有原生打开器的部署也一样。若 SSH 转发让远端 Host 看似处于本机 loopback,部署必须为网关设置 `nativeOpen: false`;无界面的 macOS/Windows Host、Windows interop 不可用的 WSL,或 display/opener 探测误报的 Linux 桌面也必须这样配置。识别操作者实际可见的桌面仍属于部署策略。
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-client-ui-deliverables",
|
||||
"description": "Produced-files turn tail: the deliverables row a finished turn ends with",
|
||||
"description": "Produced-files turn tail and clickable final-response file references for Web",
|
||||
"version": "0.0.1-rc.2",
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
@@ -55,6 +55,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -65,6 +66,7 @@
|
||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-system-prompt": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^"
|
||||
},
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
/**
|
||||
* Deliverables plugin, node half. Pure UI plugin: the empty apply exists so
|
||||
* the plugin appears in the host cordis.yml / Loader; the browser half ships
|
||||
* via exports["./client"], discovered through the package.json dsh.client
|
||||
* declaration.
|
||||
* Deliverables plugin, node half. Registers the response-format guidance that
|
||||
* lets the browser half recognize final-response file references. The browser
|
||||
* half ships via exports["./client"], discovered through the package.json
|
||||
* dsh.client declaration.
|
||||
*/
|
||||
|
||||
/** Host plugin body — no host-side behavior for this surface plugin. */
|
||||
export function apply(): void {}
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
|
||||
/** Services required for the model guidance paired with the browser renderer. */
|
||||
export const inject = ['systemPrompt']
|
||||
|
||||
/** Stable final-response guidance owned by the matching renderer. */
|
||||
const FILE_REFERENCE_PROMPT = 'When you successfully create or modify files, mention the primary outputs in your final response. '
|
||||
+ 'To make those and any other changed-file references clickable in Web, format them as Markdown inline code using the exact file-tool path, or a basename when unique among the files changed in that turn.'
|
||||
|
||||
/**
|
||||
* Register model guidance for the file-reference renderer shipped by this package.
|
||||
* @param ctx - host context carrying the system-prompt registry.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
ctx.systemPrompt.section({
|
||||
name: 'ui:deliverable-file-references',
|
||||
order: 190,
|
||||
text: FILE_REFERENCE_PROMPT,
|
||||
})
|
||||
}
|
||||
@@ -15,10 +15,9 @@ export const name = 'client-ui-deliverables-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: one slot registration and one dictionary
|
||||
* registration, both effect-owned with disposal proven by the HMR-safety
|
||||
* spec — the plugin emits no cordis events and owns no cross-plugin mutable
|
||||
* state.
|
||||
* No runtime invariant: the prompt section, slot, dictionary, event
|
||||
* definition, and optional service registrations are effect-owned with
|
||||
* disposal proven by their plugin specs; this package owns no mutable state.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
type DeliverablesTurnData,
|
||||
} from '../src/client/turn-deliverables.ts'
|
||||
import { apply, inject } from '../src/client/index.ts'
|
||||
import { apply as applyNode } from '../src/index.ts'
|
||||
import { apply as applyInvariant } from '../src/invariant.ts'
|
||||
import { en, zh } from '../src/client/locales.ts'
|
||||
|
||||
@@ -438,9 +437,7 @@ describe('producedFileMentions resolver', () => {
|
||||
})
|
||||
|
||||
describe('package shells', () => {
|
||||
it('the node half mounts inert and the invariant companion registers ownership', async () => {
|
||||
// The node half is deliberately inert; mounting it must simply not throw.
|
||||
applyNode()
|
||||
it('the invariant companion registers ownership', async () => {
|
||||
const registered: string[] = []
|
||||
const ctx = new Context()
|
||||
ctx.provide('invariants')
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/** Node-half coverage for the model guidance paired with Web file references. */
|
||||
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import { apply, inject } from '../src/index.ts'
|
||||
|
||||
let ctx: Context | undefined
|
||||
|
||||
afterEach(async () => {
|
||||
await ctx?.fiber.dispose()
|
||||
ctx = undefined
|
||||
})
|
||||
|
||||
describe('ui-deliverables node plugin', () => {
|
||||
it('registers final-response file-reference guidance only while mounted', async () => {
|
||||
ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, { persona: '' })
|
||||
const mounted = ctx.plugin({ apply, inject })
|
||||
await mounted.await()
|
||||
|
||||
const section = (await ctx.systemPrompt.assemble()).sections
|
||||
.find(entry => entry.name === 'ui:deliverable-file-references')
|
||||
expect(section?.text).toMatchInlineSnapshot('"When you successfully create or modify files, mention the primary outputs in your final response. To make those and any other changed-file references clickable in Web, format them as Markdown inline code using the exact file-tool path, or a basename when unique among the files changed in that turn."')
|
||||
|
||||
await mounted.dispose()
|
||||
expect((await ctx.systemPrompt.assemble()).sections
|
||||
.some(entry => entry.name === 'ui:deliverable-file-references')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -25,6 +25,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
},
|
||||
{
|
||||
"path": "../../core/system-prompt"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user