From 5c2826eae5986d228113c9ba84bb869b575c4b5c Mon Sep 17 00:00:00 2001
From: imccyu <276526105+imccyu@users.noreply.github.com>
Date: Fri, 30 Oct 2026 13:59:08 +0800
Subject: [PATCH] Revert "feat(client): expose stable composer prefill API"
---
.../composer-prefill-method/guide.i18n.yaml | 16 ---
.../composer-prefill-method/guide.md | 18 ---
.../composer-prefill-method/guide.zh.md | 18 ---
.../ui-chat/tests/chat-view.client.spec.tsx | 1 -
.../client/ui-conversation/README.i18n.yaml | 11 +-
packages/client/ui-conversation/README.md | 10 --
packages/client/ui-conversation/README.zh.md | 10 --
.../src/client/contract/input.ts | 36 ------
.../ui-conversation/src/client/index.ts | 3 +-
.../src/client/input/editor/runtime.ts | 32 +----
.../src/client/input/facade.ts | 24 +---
.../tests/draft-document.client.spec.ts | 38 ------
.../tests/input-scenarios.client.spec.tsx | 117 ------------------
.../ui-sidebar-files/tests/mount.client.tsx | 1 -
.../tests/expand-button.client.spec.tsx | 3 +-
.../tests/guide-body.client.spec.tsx | 3 +-
.../tests/terminal-guide.client.spec.tsx | 3 +-
.../tests/conversation-ui.client.spec.tsx | 1 -
.../ui-trajectory/tests/views.client.spec.tsx | 2 -
.../tests/plan-review-panel.client.spec.tsx | 1 -
.../user-questions-composer.client.spec.tsx | 1 -
.../tests/workflow-run.client.spec.tsx | 1 -
.../tests/voice-input.client.spec.tsx | 1 -
.../ptc-runtime-node/tests/runtime.spec.ts | 9 +-
.../tests/header-action.client.spec.tsx | 3 +-
25 files changed, 17 insertions(+), 346 deletions(-)
delete mode 100644 docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.i18n.yaml
delete mode 100644 docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.md
delete mode 100644 docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.zh.md
diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.i18n.yaml b/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.i18n.yaml
deleted file mode 100644
index e5e46fe351..0000000000
--- a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.i18n.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading
-# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
-# After editing either side, bring the other along and re-record with:
-# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.md
-/:
- en: a99282dc38a7dfeb
- zh: 6f6ee2170907eb26
-/composer-prefill-method-on-input-actions:
- en: 976e77fa72fdfdd2
- zh: dbaa50bd205dc1f3
-/composer-prefill-method-on-input-actions/change:
- en: 5186bc4ec9254340
- zh: ff0e406a96d5c50b
-/composer-prefill-method-on-input-actions/migration:
- en: 21625abfcb53c507
- zh: 4a6f0aaa04eb3a2c
diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.md b/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.md
deleted file mode 100644
index b275cb5754..0000000000
--- a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-kind: upgrade-guide
-description: "Client code that builds InputActions or implements SessionInput must provide the required prefill method."
----
-
-# Composer prefill method on input actions
-
-English | [中文](guide.zh.md)
-
-## Change
-
-`InputActions` and `SessionInput` in `@deepseek-ai/dsh-client-ui-conversation/client` now require `prefill(text, options?)`, and the package exports `ComposerPrefillMode` and `ComposerPrefillOptions`. Slot components and Session-scope plugins read prefill from the harness, so their call sites compile unchanged. Code that constructs an `InputActions` value, or implements `SessionInput`, including test doubles passed to client components, no longer satisfies the interfaces until it adds the member. The method returns whether the draft accepted the write; returning `false` is valid for doubles that do not support prefill.
-
-## Migration
-
-1. Add `prefill` to every hand-written `InputActions` or `SessionInput` object. Forward to the harness-provided action, or return `false` when the double does not support writing.
-2. Import `ComposerPrefillOptions` or `ComposerPrefillMode` from `@deepseek-ai/dsh-client-ui-conversation/client` when a signature needs them.
-3. Rebuild custom Client plugins and their test suites, then run the type check. A passing check confirms the interface is satisfied; call `inputActions.prefill('text')` from a slot component to verify the harness path when the object forwards the operation.
diff --git a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.zh.md b/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.zh.md
deleted file mode 100644
index baad8a7de4..0000000000
--- a/docs/upgrade-guide/v0.2.2-alpha.2/composer-prefill-method/guide.zh.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-kind: upgrade-guide
-description: "手写 InputActions 或实现 SessionInput 的 Client 代码必须提供必选方法 prefill。"
----
-
-# 输入操作上的 Composer 预填方法
-
-[English](guide.md) | 中文
-
-## 变更
-
-`@deepseek-ai/dsh-client-ui-conversation/client` 中的 `InputActions` 和 `SessionInput` 现在要求提供 `prefill(text, options?)`,该包同时导出 `ComposerPrefillMode` 和 `ComposerPrefillOptions`。Slot 组件与 Session 作用域插件从 Harness 读取预填操作,调用方无需改动即可编译。构造 `InputActions` 值或实现 `SessionInput` 的代码(包括传给 Client 组件的测试替身)必须新增该成员才能满足接口。该方法返回草稿是否接受写入;不支持预填的替身可以返回 `false`。
-
-## 迁移
-
-1. 在所有手写的 `InputActions` 或 `SessionInput` 对象中加入 `prefill`。转发到 Harness 提供的操作,或在替身不支持写入时返回 `false`。
-2. 签名需要时,从 `@deepseek-ai/dsh-client-ui-conversation/client` 导入 `ComposerPrefillOptions` 或 `ComposerPrefillMode`。
-3. 重新构建自定义 Client 插件及其测试,然后运行类型检查。检查通过即表示接口已满足;当对象转发该操作时,可从 Slot 组件调用 `inputActions.prefill('text')` 验证 Harness 路径。
diff --git a/packages/client/ui-chat/tests/chat-view.client.spec.tsx b/packages/client/ui-chat/tests/chat-view.client.spec.tsx
index aab9a1d2da..2bc8286e93 100644
--- a/packages/client/ui-chat/tests/chat-view.client.spec.tsx
+++ b/packages/client/ui-chat/tests/chat-view.client.spec.tsx
@@ -525,7 +525,6 @@ function makeHarness(
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: () => {},
- prefill: () => false,
persistDraft: () => {},
addAttachments: () => true,
removeAttachment: () => {},
diff --git a/packages/client/ui-conversation/README.i18n.yaml b/packages/client/ui-conversation/README.i18n.yaml
index eeae778f15..f17358e4b5 100644
--- a/packages/client/ui-conversation/README.i18n.yaml
+++ b/packages/client/ui-conversation/README.i18n.yaml
@@ -12,8 +12,8 @@
en: 8811c28013fdc6dc
zh: 0a86edc422b38ba8
/deepseek-ai-dsh-client-ui-conversation/table-of-contents:
- en: c1ca3d169bea2ccc
- zh: 1892939a2ec76e1e
+ en: eef4f18c3972ccc4
+ zh: 6be65c6ac84ce497
/deepseek-ai-dsh-client-ui-conversation/conversation-assembly:
en: ef43c4f149195939
zh: 49fc6c83fd3a05ce
@@ -24,11 +24,8 @@
en: 99ec447f0a6bda48
zh: 922cf64666676530
/deepseek-ai-dsh-client-ui-conversation/temporary-composer-entries:
- en: 9acb3b1d7f279d97
- zh: 8544b7a140437553
-/deepseek-ai-dsh-client-ui-conversation/plugin-prefill-api:
- en: 0d16e9363a821bc6
- zh: eca2ba2076a3ca9d
+ en: 59cd63057dfb6eeb
+ zh: b2e2c86e810b7bb8
/deepseek-ai-dsh-client-ui-conversation/model-experience:
en: e0a05b73ba3e4318
zh: dc85ab8433bb4456
diff --git a/packages/client/ui-conversation/README.md b/packages/client/ui-conversation/README.md
index 7982114736..14f54dc287 100644
--- a/packages/client/ui-conversation/README.md
+++ b/packages/client/ui-conversation/README.md
@@ -19,7 +19,6 @@ Desktop product events use the optional [product analytics service](../product-a
- [Sent user text](#sent-user-text)
- [Shell and standard props](#shell-and-standard-props)
- [Temporary composer entries](#temporary-composer-entries)
-- [Plugin prefill API](#plugin-prefill-api)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
@@ -187,15 +186,6 @@ The selector must be a pure function of the owner currency. Its non-null return
Session title context gestures use the shared [context menu](../ui-context-menu/README.md) with the clicked breadcrumb’s Session. The reusable image gallery receives `getTriggerProps` from its Factory’s injected `useContextMenu` hook.
-
-## Plugin prefill API
-
-A plugin can write reviewable text into the current composer through the stable `prefill` action. Slot components receive it as `inputActions.prefill(text, options)`. A session-scope plugin resolves the same action with `ctx.conversation.input.for(actx).prefill(text, options)`.
-
-The exported `ComposerPrefillOptions` type and `InputActions.prefill` signature are defined in the [input contracts](src/client/contract/input.ts).
-
-An empty string is ignored and returns `false`. An empty or whitespace-only draft, or `mode: 'replace'`, replaces the whole draft only while the composer is in the plain phase and the session is live; that replacement is its own undo step. Otherwise the text replaces the current selection in one undo step, preceded by `separator` when the editor text before the selection ends in non-whitespace; the separator defaults to a newline and an empty separator inserts verbatim. A rejected write — a non-plain replacement, a changed draft revision, a locked submission phase, or a disposed session — returns `false` without changing the draft. `prefill` never sends the draft.
-
## Model Experience
diff --git a/packages/client/ui-conversation/README.zh.md b/packages/client/ui-conversation/README.zh.md
index fe33ee7860..473e15825e 100644
--- a/packages/client/ui-conversation/README.zh.md
+++ b/packages/client/ui-conversation/README.zh.md
@@ -19,7 +19,6 @@ kind: "package-reference"
- [已发送用户文本](#sent-user-text)
- [Shell 与标准 props](#shell-and-standard-props)
- [临时 composer entry](#temporary-composer-entries)
-- [插件预填 API](#plugin-prefill-api)
- [模型体验](#model-experience)
- [已知限制与暂缓事项](#known-limitations-and-deferred-work)
- [开发备注](#dev-note)
@@ -187,15 +186,6 @@ selector 必须是 owner currency 的纯函数。非 null 返回值作为 `match
会话标题右键使用共享[右键菜单](../ui-context-menu/README.zh.md),目标是被点击面包屑所属会话。可复用图片库从其 Factory 注入的 `useContextMenu` hook 获取 `getTriggerProps`。
-
-## 插件预填 API
-
-插件可以通过稳定的 `prefill` 操作把可检查的文本写入当前 Composer。Slot 组件收到的是 `inputActions.prefill(text, options)`;Session 作用域的插件通过 `ctx.conversation.input.for(actx).prefill(text, options)` 解析同一个操作。
-
-导出的 `ComposerPrefillOptions` 类型和 `InputActions.prefill` 签名定义在[输入接口](src/client/contract/input.ts)中。
-
-空字符串会被忽略并返回 `false`。草稿为空或只有空白,或者使用 `mode: 'replace'` 时,只有在 Composer 处于 plain 阶段且 Session 仍存活时才替换整个草稿;这次替换本身就是独立的撤销步骤。否则文本会替换当前选区,作为一次可撤销编辑;若选区前的编辑器文本末尾不是空白,则先插入 `separator`。`separator` 默认是换行,传空字符串时原样插入。非 plain 阶段的替换、草稿版本已变、提交阶段锁定或 Session 已释放时,写入会被拒绝并返回 `false`,草稿保持不变。`prefill` 不会发送草稿。
-
## 模型体验
diff --git a/packages/client/ui-conversation/src/client/contract/input.ts b/packages/client/ui-conversation/src/client/contract/input.ts
index e0c93121e1..82f030c775 100644
--- a/packages/client/ui-conversation/src/client/contract/input.ts
+++ b/packages/client/ui-conversation/src/client/contract/input.ts
@@ -187,15 +187,6 @@ export interface SessionInput extends InputTarget {
/** Replace the whole draft with plain text, removing inline reference chips. */
setDraft(text: string): void
- /**
- * Write plugin-owned text into the draft in one undo step. This is the
- * session-scope entry to the same prefill operation exposed as
- * {@link InputActions.prefill} to slot components.
- * @param text - text to write; an empty string is ignored.
- * @param options - replacement mode and insertion separator.
- * @returns whether the draft accepted the write.
- */
- prefill(text: string, options?: ComposerPrefillOptions): boolean
/** Append ordered browser-owned attachment ids; busy admission phases refuse. */
addAttachments(ids: readonly DraftAttachmentId[]): boolean
/** Remove one browser-owned attachment id; busy admission phases refuse. @returns whether the id was removed. */
@@ -258,20 +249,6 @@ export interface DraftInitializationOptions {
/** An initialization request never waits for a React mount or a remote catalog. */
export type DraftInitializationResult = 'applied' | 'preserved' | 'blocked'
-/** How a composer prefill treats existing draft text. */
-export type ComposerPrefillMode = 'insert' | 'replace'
-
-/** Options for a plugin-owned composer prefill. */
-export interface ComposerPrefillOptions {
- /** Replace the whole draft instead of inserting at the caret; defaults to `insert`. */
- readonly mode?: ComposerPrefillMode
- /**
- * Text placed before an inserted prefill when the caret follows non-whitespace.
- * Defaults to a newline; an empty string inserts verbatim. Ignored when replacing.
- */
- readonly separator?: string
-}
-
/**
* The public input action face provided to every session-scope slot
* component: stable-identity callbacks, mirroring the
@@ -292,19 +269,6 @@ export interface InputActions {
insertText(text: string, span: TokenSpan): boolean
/** Replace the whole draft with plain text, removing inline reference chips. */
setDraft(text: string): void
- /**
- * Write plugin-owned text into the draft in one undo step. An empty or
- * whitespace-only draft, or `mode: 'replace'`, replaces the draft only while
- * the session is live and the composer is in the plain phase; otherwise the
- * current selection is replaced by an insertion at the selection, preceded
- * by `separator` when the editor text before it ends in non-whitespace.
- * Replacement edits are independent undo steps.
- * @param text - text to write; an empty string is ignored.
- * @param options - replacement mode and insertion separator.
- * @returns whether the draft accepted the write; false for an empty string,
- * a non-plain replacement, a changed draft revision, or a disposed session.
- */
- prefill(text: string, options?: ComposerPrefillOptions): boolean
/** Write the current semantic document through the mounted persistence callback. */
persistDraft(): void
/** Append ordered browser-owned attachment ids; busy admission phases refuse. */
diff --git a/packages/client/ui-conversation/src/client/index.ts b/packages/client/ui-conversation/src/client/index.ts
index a2eb26cd6f..7a39200c30 100644
--- a/packages/client/ui-conversation/src/client/index.ts
+++ b/packages/client/ui-conversation/src/client/index.ts
@@ -98,8 +98,7 @@ export type {
UseConversationViews,
} from './contract/slots.ts'
export type {
- BeginCommandRequest, CommandClaim, ComposerPrefillMode, ComposerPrefillOptions,
- ConsumeTokenRequest, DraftAttachmentId, DraftInitializationOptions,
+ BeginCommandRequest, CommandClaim, ConsumeTokenRequest, DraftAttachmentId, DraftInitializationOptions,
DraftInitializationResult, InputActions,
InputState, InsertReferenceRequest, InsertTextRequest, PickOutcome, SessionInput,
SessionInputResolver, SubmitAttachment, SubmitOutcome,
diff --git a/packages/client/ui-conversation/src/client/input/editor/runtime.ts b/packages/client/ui-conversation/src/client/input/editor/runtime.ts
index 1a66543018..f162943b6f 100644
--- a/packages/client/ui-conversation/src/client/input/editor/runtime.ts
+++ b/packages/client/ui-conversation/src/client/input/editor/runtime.ts
@@ -205,26 +205,6 @@ export class DraftEditorRuntime {
}, { discrete: true, tag: HISTORY_MERGE_TAG })
}
- /**
- * Replace the whole draft as one independent undo step at the top level.
- * Unlike the seed {@link setDraft}, this edit carries a history boundary so
- * the user can undo the plugin prefill without undoing an earlier seed or
- * edit. Nested calls join their enclosing editor transaction.
- * @param text - the full next draft.
- */
- replaceDraft(text: string): void {
- const clean = text.replace(REFERENCE_PLACEHOLDER_RE, '')
- if (clean === this.projection.clipboardText && this.projection.occurrences.length === 0 && this.projection.quotes.length === 0) return
- // An empty editor has no earlier entry to undo to; seed one before the first prefill.
- if (this.editor.getEditorState().isEmpty()) {
- this.applyEdit(() => { $getRoot().append($createParagraphNode()) }, HISTORY_MERGE_TAG)
- }
- this.applyEdit(() => {
- $getRoot().clear()
- $appendParagraphs(clean)
- }, PASTE_TAG)
- }
-
/**
* Add plain text after the current draft as new paragraphs, keeping its text
* and references; an empty draft takes the text as its whole content.
@@ -273,15 +253,9 @@ export class DraftEditorRuntime {
* @returns the ordered [start, end) span in detect coordinates.
*/
caretSpan(): { start: number; end: number } {
- let span = { start: 0, end: 0 }
- // A snapshot read misses uncommitted native selection. A discrete update
- // synchronizes it; nested calls read the enclosing update's pending state.
- this.applyEdit(() => {
- const projection = $projectComposer(key => this.occurrenceIdOf(key))
- const at = projection.detectText.length
- span = projection.selection ?? { start: at, end: at }
- })
- return span
+ if (this.projection.selection !== null) return this.projection.selection
+ const at = this.projection.detectText.length
+ return { start: at, end: at }
}
/**
diff --git a/packages/client/ui-conversation/src/client/input/facade.ts b/packages/client/ui-conversation/src/client/input/facade.ts
index 2012a02eeb..6306216b1c 100644
--- a/packages/client/ui-conversation/src/client/input/facade.ts
+++ b/packages/client/ui-conversation/src/client/input/facade.ts
@@ -15,7 +15,7 @@ import {
} from '@deepseek-ai/dsh-client-store'
import type { LexicalEditor } from 'lexical'
import type {
- CommandClaim, ComposerPrefillOptions, ConsumeTokenRequest, DraftAttachmentId, DraftInitializationOptions, DraftInitializationResult,
+ CommandClaim, ConsumeTokenRequest, DraftAttachmentId, DraftInitializationOptions, DraftInitializationResult,
InputActions, InputEffect, InputNotice, InputState, InputTriggerController, PickOutcome,
SessionInput, SubmitAttempt, SubmitAttachment, SubmitOutcome,
} from '../contract/input.ts'
@@ -148,7 +148,6 @@ export class SessionInputShell implements SessionInput {
return this.draftEditor.insertAsyncText(span, text)
},
setDraft: (text) => { this.setDraft(text) },
- prefill: (text, options) => this.prefill(text, options),
persistDraft: () => { this.persistCurrentDraft() },
addAttachments: ids => this.addAttachments(ids),
removeAttachment: (id) => { this.removeAttachment(id) },
@@ -239,27 +238,6 @@ export class SessionInputShell implements SessionInput {
return inserted
}
- /**
- * Write plugin-owned text into the draft in one undo step.
- * @param text - text to write; an empty string is ignored.
- * @param options - replacement mode and insertion separator.
- * @returns whether the draft accepted the write.
- */
- prefill(text: string, options: ComposerPrefillOptions = {}): boolean {
- if (text.length === 0) return false
- if ((options.mode ?? 'insert') === 'replace' || this.snapshot.draft.trim() === '') {
- if (this.snapshot.phase !== 'plain' || this.disposed) return false
- this.cancelCommandRestore()
- this.draftEditor.replaceDraft(text)
- return true
- }
- const span = this.actions.captureInsertion()
- const separator = options.separator ?? '\n'
- const preceding = this.projection.detectText.slice(0, span.start)
- const prefix = separator.length > 0 && span.start > 0 && !/\s$/.test(preceding) ? separator : ''
- return this.actions.insertText(`${prefix}${text}`, span)
- }
-
/**
* Reconnect optional catalog notifications and resume a pending command
* chip restore without waiting for another edit.
diff --git a/packages/client/ui-conversation/tests/draft-document.client.spec.ts b/packages/client/ui-conversation/tests/draft-document.client.spec.ts
index 44b67bf92b..8e50e1ad3a 100644
--- a/packages/client/ui-conversation/tests/draft-document.client.spec.ts
+++ b/packages/client/ui-conversation/tests/draft-document.client.spec.ts
@@ -818,44 +818,6 @@ describe('draft documents in the input shell', () => {
)
})
-describe('plugin prefill history', () => {
- it.each(['insert', 'replace'] as const)('keeps the first %s prefill undoable before the editor mounts', async (mode) => {
- const shell = makeShell()
- expect(shell.editor.getRootElement()).toBeNull()
- expect(shell.prefill('First plugin draft', { mode })).toBe(true)
- shell.editor.dispatchCommand(UNDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: '', references: [] }) })
- shell.editor.dispatchCommand(REDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: 'First plugin draft', references: [] }) })
- })
-
- it('keeps consecutive unmounted prefills as separate undo steps', async () => {
- const shell = makeShell()
- expect(shell.prefill('First')).toBe(true)
- expect(shell.prefill('Second', { mode: 'replace' })).toBe(true)
- shell.editor.dispatchCommand(UNDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: 'First', references: [] }) })
- shell.editor.dispatchCommand(UNDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: '', references: [] }) })
- shell.editor.dispatchCommand(REDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: 'First', references: [] }) })
- shell.editor.dispatchCommand(REDO_COMMAND, undefined)
- await vi.waitFor(() => { expect(shell.draftSnapshot).toEqual({ text: 'Second', references: [] }) })
- })
-
- it('leaves an ordinary empty editor untouched until a prefill arrives', () => {
- const shell = makeShell()
- const root = document.createElement('div')
- root.contentEditable = 'true'
- document.body.append(root)
- onTestFinished(() => { shell.editor.setRootElement(null); root.remove() })
- shell.editor.setRootElement(root)
- expect(shell.prefill('')).toBe(false)
- expect(root.childElementCount).toBe(0)
- expect(shell.draftSnapshot).toEqual({ text: '', references: [] })
- })
-})
-
describe('plain-text reference catalogs', () => {
it('leaves named tokens plain until their own catalog includes them', () => {
const unavailable = new Map<'/' | '@', readonly string[]>()
diff --git a/packages/client/ui-conversation/tests/input-scenarios.client.spec.tsx b/packages/client/ui-conversation/tests/input-scenarios.client.spec.tsx
index 8f7a886720..2129c605d2 100644
--- a/packages/client/ui-conversation/tests/input-scenarios.client.spec.tsx
+++ b/packages/client/ui-conversation/tests/input-scenarios.client.spec.tsx
@@ -639,120 +639,3 @@ describe('scenario I: unknown /xyz + enter', () => {
expect(b.sink).not.toHaveBeenCalled()
})
})
-
-describe('plugin composer prefill API', () => {
- it('fills empty drafts, inserts at the caret, and honors replace and separator options', async () => {
- const b = await bench()
- expect(b.shell.prefill('')).toBe(false)
- act(() => { expect(b.shell.prefill('Explain')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Explain')
- act(() => { expect(b.shell.actions.prefill('tests')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Explain\ntests')
- act(() => { expect(b.shell.prefill('next', { separator: ' ' })).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Explain\ntests next')
- act(() => { b.shell.setDraft('Keep ') })
- act(() => { expect(b.shell.prefill('Next')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Keep Next')
- act(() => { expect(b.shell.prefill('Only', { mode: 'replace' })).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Only')
- act(() => { expect(b.shell.prefill('Next', { separator: '' })).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('OnlyNext')
- act(() => {
- b.shell.editor.update(() => { $getRoot().selectStart() }, { discrete: true })
- })
- act(() => { expect(b.shell.prefill('Head')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('HeadOnlyNext')
- })
-
- it('reports a rejected insertion without changing the draft', async () => {
- const b = await bench()
- act(() => { b.shell.setDraft('Busy') })
- const insertText = vi.spyOn(b.shell.actions, 'insertText').mockReturnValue(false)
- act(() => { expect(b.shell.prefill('Next')).toBe(false) })
- expect(b.shell.snapshot.draft).toBe('Busy')
- insertText.mockRestore()
- })
-})
-
-describe('plugin composer prefill API guards and history', () => {
- it('makes a replacement its own undo step', async () => {
- const b = await bench()
- act(() => { b.shell.setDraft('Old') })
- act(() => { expect(b.shell.prefill('New', { mode: 'replace' })).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('New')
- act(() => { b.shell.editor.dispatchCommand(UNDO_COMMAND, undefined) })
- await act(async () => {})
- expect(b.shell.snapshot.draft).toBe('Old')
- })
-
- it('refuses replacement while sending and after disposal', async () => {
- const sending = await bench()
- act(() => { sending.shell.setDraft('Sent') })
- act(() => { sending.shell.submit() })
- expect(sending.shell.snapshot.phase).toBe('sending')
- act(() => { expect(sending.shell.prefill('New', { mode: 'replace' })).toBe(false) })
-
- const disposed = await bench()
- act(() => { disposed.shell.setDraft('Keep') })
- disposed.shell.dispose()
- act(() => { expect(disposed.shell.prefill('New', { mode: 'replace' })).toBe(false) })
- act(() => { expect(disposed.shell.prefill('New')).toBe(false) })
- })
-
- it('checks the separator against detect coordinates around a reference chip', async () => {
- const b = await bench()
- act(() => { b.shell.setDraft('@res') })
- const accepted = b.shell.insertReference({
- source: 'reference',
- ref: '@[Research](dsh-session:InNvdXJjZSI)',
- label: 'Research',
- clipboardText: '@[Research](dsh-session:InNvdXJjZSI)',
- }, {
- start: 0,
- end: 4,
- draftRev: b.shell.snapshot.draftRev,
- })
- expect(accepted).toBe(true)
- const afterChip = b.shell.snapshot.draft
- expect(afterChip.endsWith(' ')).toBe(true)
- act(() => { expect(b.shell.prefill('Next')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe(`${afterChip}Next`)
- })
-
- it('replaces a whitespace-only draft', async () => {
- const b = await bench()
- act(() => { b.shell.setDraft(' \n ') })
- act(() => { expect(b.shell.prefill('Text')).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('Text')
- })
-
- it.each(['captureInsertion', 'prefill'] as const)('reads the native caret through %s before its selection is projected', async (entry) => {
- const b = await bench()
- act(() => { b.shell.setDraft('Keep text') })
- b.textarea.focus()
- const text = b.textarea.firstChild?.firstChild
- const selection = document.getSelection()
- if (text === undefined || text === null || selection === null) throw new Error('missing native draft selection')
- selection.setBaseAndExtent(text, 0, text, 0)
- const before = b.shell.draftSnapshot
- if (entry === 'captureInsertion') {
- const revision = b.shell.snapshot.draftRev
- const track = vi.spyOn(b.controller, 'track')
- const expected = { start: 0, end: 0, draftRev: revision }
- expect(b.shell.actions.captureInsertion()).toEqual(expected)
- expect(track).toHaveBeenCalledOnce()
- expect(b.shell.actions.captureInsertion()).toEqual(expected)
- expect(track).toHaveBeenCalledOnce()
- expect(b.shell.draftSnapshot).toBe(before)
- expect(b.shell.snapshot.draftRev).toBe(revision)
- expect(b.sink).not.toHaveBeenCalled()
- return
- }
- act(() => { expect(b.shell.prefill('Head', { separator: '' })).toBe(true) })
- expect(b.shell.snapshot.draft).toBe('HeadKeep text')
- act(() => { b.shell.editor.dispatchCommand(UNDO_COMMAND, undefined) })
- await act(async () => {})
- expect(b.shell.draftSnapshot).toEqual(before)
- expect(b.sink).not.toHaveBeenCalled()
- })
-})
diff --git a/packages/client/ui-sidebar-files/tests/mount.client.tsx b/packages/client/ui-sidebar-files/tests/mount.client.tsx
index f4939367a9..35a660f264 100644
--- a/packages/client/ui-sidebar-files/tests/mount.client.tsx
+++ b/packages/client/ui-sidebar-files/tests/mount.client.tsx
@@ -242,7 +242,6 @@ const unusedFactoryProps = {
captureInsertion: unusedFrameworkProp,
insertText: unusedFrameworkProp,
setDraft: unusedFrameworkProp,
- prefill: unusedFrameworkProp,
persistDraft: unusedFrameworkProp,
addAttachments: unusedFrameworkProp,
removeAttachment: unusedFrameworkProp,
diff --git a/packages/client/ui-sidebar-right/tests/expand-button.client.spec.tsx b/packages/client/ui-sidebar-right/tests/expand-button.client.spec.tsx
index fee4cd7840..205ebcf35f 100644
--- a/packages/client/ui-sidebar-right/tests/expand-button.client.spec.tsx
+++ b/packages/client/ui-sidebar-right/tests/expand-button.client.spec.tsx
@@ -29,8 +29,7 @@ const standard: GlobalStandardProps & SessionStandardProps = {
useSessionRetainInfo: unused, useResource: unused, useWorkspaces: unused,
...allowAllAccess,
useContextMenu: contextMenuHook(),
- inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused,
- setDraft: unused, prefill: unused, persistDraft: unused,
+ inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused, setDraft: unused, persistDraft: unused,
addAttachments: unused, removeAttachment: unused, pruneAttachments: unused, submit: unused },
}
diff --git a/packages/client/ui-sidebar-right/tests/guide-body.client.spec.tsx b/packages/client/ui-sidebar-right/tests/guide-body.client.spec.tsx
index c9ad11215f..b8ed43cf9c 100644
--- a/packages/client/ui-sidebar-right/tests/guide-body.client.spec.tsx
+++ b/packages/client/ui-sidebar-right/tests/guide-body.client.spec.tsx
@@ -36,8 +36,7 @@ const standard: GlobalStandardProps & SessionStandardProps = {
useSessionRetainInfo: unused, useResource: unused, useWorkspaces: unused,
...allowAllAccess,
useContextMenu: contextMenuHook(),
- inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused,
- setDraft: unused, prefill: unused, persistDraft: unused,
+ inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused, setDraft: unused, persistDraft: unused,
addAttachments: unused, removeAttachment: unused, pruneAttachments: unused, submit: unused },
}
diff --git a/packages/client/ui-sidebar-terminal/tests/terminal-guide.client.spec.tsx b/packages/client/ui-sidebar-terminal/tests/terminal-guide.client.spec.tsx
index ba63e2dd3f..19a6c0210c 100644
--- a/packages/client/ui-sidebar-terminal/tests/terminal-guide.client.spec.tsx
+++ b/packages/client/ui-sidebar-terminal/tests/terminal-guide.client.spec.tsx
@@ -25,8 +25,7 @@ const standard: GlobalStandardProps & SessionStandardProps = {
useSessionRetainInfo: unused, useResource: unused, useWorkspaces: unused,
...allowAllAccess,
useContextMenu: contextMenuHook(),
- inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused,
- setDraft: unused, prefill: unused, persistDraft: unused,
+ inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused, setDraft: unused, persistDraft: unused,
addAttachments: unused, removeAttachment: unused, pruneAttachments: unused, submit: unused },
}
const choices: TerminalLaunchShells = {
diff --git a/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx b/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
index bff439df98..3ac3a7b325 100644
--- a/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
+++ b/packages/client/ui-subagent/tests/conversation-ui.client.spec.tsx
@@ -107,7 +107,6 @@ function props(
captureInsertion: unused,
insertText: unused,
setDraft: unused,
- prefill: unused,
persistDraft: unused,
addAttachments: unused,
removeAttachment: unused,
diff --git a/packages/client/ui-trajectory/tests/views.client.spec.tsx b/packages/client/ui-trajectory/tests/views.client.spec.tsx
index 1da25650d1..9b61aaef03 100644
--- a/packages/client/ui-trajectory/tests/views.client.spec.tsx
+++ b/packages/client/ui-trajectory/tests/views.client.spec.tsx
@@ -220,7 +220,6 @@ function standaloneProps(
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: () => {},
- prefill: () => false,
persistDraft: () => {},
addAttachments: () => false,
removeAttachment: () => {},
@@ -359,7 +358,6 @@ function mount(fixture: Awaited>) {
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: vi.fn(),
- prefill: vi.fn(() => false),
persistDraft: vi.fn(),
addAttachments: vi.fn(() => false),
removeAttachment: vi.fn(),
diff --git a/packages/client/ui-user-questions/tests/plan-review-panel.client.spec.tsx b/packages/client/ui-user-questions/tests/plan-review-panel.client.spec.tsx
index d767bd695e..41e0e6902f 100644
--- a/packages/client/ui-user-questions/tests/plan-review-panel.client.spec.tsx
+++ b/packages/client/ui-user-questions/tests/plan-review-panel.client.spec.tsx
@@ -137,7 +137,6 @@ const kit: Omit = {
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: () => { throw new Error('unused') },
- prefill: () => false,
persistDraft: () => { throw new Error('unused') },
addAttachments: () => { throw new Error('unused') },
removeAttachment: () => { throw new Error('unused') },
diff --git a/packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx b/packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx
index b5a1167643..b3fdffda6b 100644
--- a/packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx
+++ b/packages/client/ui-user-questions/tests/user-questions-composer.client.spec.tsx
@@ -143,7 +143,6 @@ const kitBase: Omit ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: () => { throw new Error('unused') },
- prefill: () => false,
persistDraft: () => { throw new Error('unused') },
addAttachments: () => { throw new Error('unused') },
removeAttachment: () => { throw new Error('unused') },
diff --git a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
index c088085b61..9ba88045d8 100644
--- a/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
+++ b/packages/client/ui-workflow-run/tests/workflow-run.client.spec.tsx
@@ -355,7 +355,6 @@ function panelProps(data: WorkflowRunChatData, sessions = listState(), openSessi
captureInsertion: () => ({ start: 0, end: 0, draftRev: 0 }),
insertText: () => false,
setDraft: () => {},
- prefill: () => false,
persistDraft: () => {},
addAttachments: () => false,
removeAttachment: () => {},
diff --git a/packages/experimental/client-ui-voice-input/tests/voice-input.client.spec.tsx b/packages/experimental/client-ui-voice-input/tests/voice-input.client.spec.tsx
index bef03240e9..b0e23ff464 100644
--- a/packages/experimental/client-ui-voice-input/tests/voice-input.client.spec.tsx
+++ b/packages/experimental/client-ui-voice-input/tests/voice-input.client.spec.tsx
@@ -24,7 +24,6 @@ function fixture(recording?: Recording, navigable = true) {
const inputActions = { notify: vi.fn(), insertQuote: () => false,
captureInsertion: vi.fn(() => ({ start: 3, end: 3, draftRev: 1 })), insertText: vi.fn(() => true),
setDraft: vi.fn(), persistDraft: vi.fn(), addAttachments: vi.fn(() => true), removeAttachment: vi.fn(),
- prefill: vi.fn(() => false),
pruneAttachments: vi.fn(), submit: vi.fn() }
const readiness = createSnapshotStore({ connected: true, error: null, catalog: {
providers: [{ id, name: 'SenseVoiceSmall', location: 'host-local', languages: ['auto', 'zh', 'en', 'ja'], preparation: { phase: 'ready' } }],
diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts
index 0675b63893..03688e1ae4 100644
--- a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts
+++ b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts
@@ -182,12 +182,11 @@ describe('Node program process', () => {
})
it.each([{}, { timeoutMs: null }] as const)('cancels a live program and closes its managed process with %j', async (timing) => {
- const { run, cwd } = await setup()
+ const { run } = await setup()
+ const entered = Promise.withResolvers()
const controller = new AbortController()
- // Synchronous file readiness does not depend on the busy child's control pipe draining.
- const active = run({ ...timing, program: 'const fs = await import("node:fs"); fs.writeFileSync("entered", "ready"); for (;;) {}', signal: controller.signal, bindings: [] })
- onTestFinished(async () => { controller.abort('test finished'); await active })
- await expect.poll(() => readFile(join(cwd, 'entered'), 'utf8')).toBe('ready')
+ const active = run({ ...timing, program: 'void tools.enter({}); for (;;) {}', signal: controller.signal, bindings: bindings({ enter: async () => { entered.resolve(undefined); return null } }) })
+ await entered.promise
controller.abort('stop')
expect((await active).error).toEqual({ kind: 'abort', message: 'stop' })
})
diff --git a/packages/session-query/session-log-export/tests/header-action.client.spec.tsx b/packages/session-query/session-log-export/tests/header-action.client.spec.tsx
index 95162fffdf..4a64ffb70d 100644
--- a/packages/session-query/session-log-export/tests/header-action.client.spec.tsx
+++ b/packages/session-query/session-log-export/tests/header-action.client.spec.tsx
@@ -27,8 +27,7 @@ const standard: GlobalStandardProps & SessionStandardProps = {
useSessionAccess: () => true, useRemoteAccess: () => true, useWorkspaceAccess: () => true,
useConversation: unused, useInput: unused, useChat: unused, useTrajectory: unused,
usePanelInfo: unused, useContextMenu: contextMenuHook(), useResource: unused, useWorkspaces: unused,
- inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused,
- setDraft: unused, prefill: unused, persistDraft: unused,
+ inputActions: { insertQuote: () => false, captureInsertion: unused, insertText: unused, setDraft: unused, persistDraft: unused,
addAttachments: unused, removeAttachment: unused, pruneAttachments: unused, submit: unused },
}