docs: turn outline rail contracts and agent note
READMEs record the rail's outline merge and the loadThrough paging verb in both languages; the feature Agent Note owns the decision, alternatives (sparse windows, minSeq wire bound, outline RPC, height estimation), and coverage map. Regenerates the client/API catalogs the widened faces feed.
This commit is contained in:
1 parent
3a834fe6c9
commit
422b603874
11 files changed
+89
-15
No files matched your search
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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 .agents/notes/implemented/feature/2026-08-30-web-turn-rail-outline-jump.md
|
||||
2026-08-30-web-turn-rail-outline-jump.md: 4984cd1325d3e44a9114b36b3245b98a34c92e99
|
||||
2026-08-30-web-turn-rail-outline-jump.zh.md: 8a27d72493c49989d33f0e7df786ff1129e66dec
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note: Full-session turn rail with outline projection and jump paging
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-30-web-turn-rail-outline-jump.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The web chat's turn rail derived its marks from the loaded event window, and the window is a paged suffix of the log (50-message tail, `Load earlier` per page). In a long session the rail therefore named only the most recent turns: history that had not been paged in was invisible to navigation, unreachable except by clicking `Load earlier` repeatedly, and the rail squeezed whatever it did show into a fixed frame by compressing mark spacing to percentages, so a many-turn session degenerated into an unreadable dense strip.
|
||||
|
||||
## Decision
|
||||
|
||||
Three cooperating pieces, each useful alone.
|
||||
|
||||
**Data: the `turnOutline` session projection.** `packages/session/session-turn-outline` registers one pure fold on `ctx.sessionProjections`: every `turn/start` appends `{turn, seq, prompt: ''}` (skipping boundaries that do not advance the turn number, keeping the outline strictly increasing), and the turn's first human `user/message` fills a 160-character prompt preview mirroring the rail's loaded-turn preview semantics. The value rides the existing projection carriers — tail-page seed, `session/projection` control frames, projcache — and the web-app bundle mounts the plugin. `seq` is the `turn/start` event seq: the loop logs it before the turn's prompt and steps, so paging a window back through that seq loads the whole turn.
|
||||
|
||||
**Paging: `Session.loadThrough(seq)`.** The session-controller client gains a jump loader beside `loadOlder()`: it loops the existing prepend pager in 200-message pages (`JUMP_PAGE_MESSAGES`) until `baseSeq <= seq`, lowers a shared low-water target when called again mid-jump, stops on a page that leaves `baseSeq` unmoved (the no-progress guard against an empty page still claiming history), and reports busy through the existing `loadingOlder` snapshot bit. No wire change: seqs are dense, so the client computes everything from `beforeSeq` arithmetic.
|
||||
|
||||
**View: merge, jump, and the fixed-pitch rail.** `mergeTurnRailItems` (ui-chat, view layer — the conversation snapshot still never carries projection values) unions the outline with the loaded rail items into `TurnRailItem`s discriminated by `anchor: loaded(key) | unloaded(seq)`; loaded wins per turn, and the outline prompt fills a mid-turn window head's empty preview. Activating an unloaded mark holds the reader's place with the existing paging anchor, calls `loadThrough`, and lands after React commits — no height estimation: a mid-paging landing pins the target row as the paging anchor so later chunks and the `Load earlier` button's unmount cannot drift it, and the loader's completion runs one final correction unless the reader already scrolled off the target (settlement otherwise repages once per head movement, then falls back to the nearest rendered turn). The rail itself keeps a fixed 10px pitch: the ladder scrolls inside the old frame geometry behind a hidden scrollbar, gradient fades mark each still-scrollable end, the hover preview compensates the rail scroll, and the active mark keeps itself centred while the pointer is off the rail. Unloaded marks render short and dimmed with a `Load and jump to turn N` label and pulse while their jump pages.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Sparse or segmented windows** (load only the target turn's neighbourhood): rejected — window contiguity is the foundation the transport validation, assembler, timeline, and scroll anchoring all share; a discontiguous window is a different architecture, deferred until sessions outgrow full paging.
|
||||
|
||||
**A `minSeq` page bound on the wire** (one targeted request instead of the client loop): rejected for v1 — the loop needs no protocol change, yields natural per-chunk progress, and Codex's TUI uses the same recursive page-pull shape for its jump-to-start; the single unbounded frame can be revisited if round trips ever dominate.
|
||||
|
||||
**A dedicated outline RPC**: rejected — the projection seam already provides the consistency cut with the tail page, live push, persistence, and capability-absence fallback that a bespoke endpoint would have to rebuild.
|
||||
|
||||
**Estimated row heights for jump positioning**: rejected — transcript rows vary wildly (tool cards, images, code), so estimation jitters; landing after commit is exact and matches Codex's deferred `pending_scroll_chunk` realization.
|
||||
|
||||
## Consequences
|
||||
|
||||
The rail is now session-scoped rather than window-scoped, at the cost of a whole-value projection that grows with the session (~200 bytes per turn, pushed at most twice per turn); splitting previews into an on-demand read is deferred until multi-thousand-turn sessions need it. A deep jump still loads every intervening page — the contiguous-window contract — so jumping to turn 1 of a huge session materializes the whole transcript, as manual paging always did. Assemblies without the projection plugin keep the old loaded-only rail. Coverage: projection unit + Loader-composition + HMR specs in the new package, `loadThrough` loop specs in session-controller, merge and jump specs in ui-chat (including the settle correction and busy lifecycle), and a browser contract in the chat-scroll e2e that drives a keyboard jump from an 88-turn fixture's tail to its unloaded first turn and asserts the landing geometry and rail fades.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Agent Note: 基于大纲投影与跳转分页的整会话轮次导航栏
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-30-web-turn-rail-outline-jump.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
Web 聊天的轮次导航栏从已加载的事件窗口推导刻度,而窗口是日志的分页后缀(50 条 message 的尾页,每次 `加载更早` 一页)。长会话里导航栏因此只列出最近的轮次:尚未分页载入的历史对导航不可见,除了反复点 `加载更早` 无法到达,且导航栏把已显示的刻度按百分比压缩进固定外框,多轮会话退化成不可读的密集条带。
|
||||
|
||||
## Decision
|
||||
|
||||
三个相互配合、各自独立可用的部分。
|
||||
|
||||
**数据:`turnOutline` 会话投影。** `packages/session/session-turn-outline` 在 `ctx.sessionProjections` 上注册一个纯 fold:每个 `turn/start` 追加 `{turn, seq, prompt: ''}`(跳过未推进轮次号的边界,保持大纲严格递增),该轮首条人类 `user/message` 填入 160 字符的提示词预览,语义与导航栏已加载轮次的预览一致。值搭现有投影载体——尾页 seed、`session/projection` 控制帧、projcache——web-app bundle 挂载该插件。`seq` 是 `turn/start` 事件的 seq:loop 先记它再记该轮的提示词与步骤,窗口向后分页越过该 seq 即载入整轮。
|
||||
|
||||
**分页:`Session.loadThrough(seq)`。** session-controller 客户端在 `loadOlder()` 旁新增跳转加载器:按 200 条 message 一页(`JUMP_PAGE_MESSAGES`)循环现有 prepend 分页器直到 `baseSeq <= seq`,跳转中再次调用会下调共享低水位目标,遇到 `baseSeq` 未动的页即停(对空页仍声称有历史的无进展守卫),忙碌状态复用现有 `loadingOlder` 快照位。零 wire 改动:seq 稠密,客户端仅凭 `beforeSeq` 算术即可。
|
||||
|
||||
**视图:合并、跳转与固定间距导航栏。** `mergeTurnRailItems`(ui-chat,视图层——会话快照仍不携带投影值)把大纲与已加载条目并成以 `anchor: loaded(key) | unloaded(seq)` 判别的 `TurnRailItem`;同轮已加载者优先,大纲提示词填补窗口头部半轮的空预览。激活未加载刻度先用现有分页锚点稳住读者位置、调用 `loadThrough`、在 React 提交后落点——不做任何高度估算:分页中途的落点把目标行钉为分页锚点,后续分片与 `加载更早` 按钮的卸载都不会使落点漂移,加载器完结时再做一次最终校正,除非读者已主动滚离目标(settlement 否则按窗口头每前进一次重发一次分页,再兜底落到最近的已渲染轮次)。导航栏本身保持固定 10px 间距:阶梯在原外框几何内隐藏滚动条滚动,渐变淡出标示仍可滚动的端点,悬浮预览补偿导航栏滚动量,指针不在栏上时活跃刻度自动保持居中。未加载刻度以短而暗的形态呈现,标签为「加载并跳转到第 N 轮」,其跳转分页期间脉冲闪烁。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**稀疏/分段窗口**(只加载目标轮附近):拒绝——窗口连续性是 transport 校验、assembler、timeline 与滚动锚定共同依赖的地基;不连续窗口是另一套架构,推迟到会话规模超出全量分页时再议。
|
||||
|
||||
**wire 上加 `minSeq` 页边界**(单发定向请求替代客户端循环):v1 拒绝——循环零协议改动、自带逐片进度,Codex TUI 的跳到开头也是同款递归拉页形态;若往返耗时成为瓶颈再重启无界单帧方案。
|
||||
|
||||
**专用大纲 RPC**:拒绝——投影 seam 已自带与尾页的一致性切面、实时推送、持久化与能力缺席回退,专用端点得重造这一切。
|
||||
|
||||
**估算行高定位跳转**:拒绝——transcript 行高方差极大(工具卡、图片、代码),估算必抖;提交后落点零误差,且与 Codex 延迟兑现的 `pending_scroll_chunk` 同构。
|
||||
|
||||
## Consequences
|
||||
|
||||
导航栏从窗口口径变为会话口径,代价是随会话增长的整值投影(约每轮 200 字节,每轮至多推送两次);把预览拆成按需读取推迟到数千轮量级的会话真正需要时。深跳仍会加载沿途所有页——连续窗口契约——跳到超长会话的第 1 轮会实体化整个 transcript,与手动翻页的终态相同。未挂载该投影插件的装配保留旧的仅已加载导航。覆盖:新包的投影单元 + Loader 组合 + HMR 测试、session-controller 的 loadThrough 循环测试、ui-chat 的合并与跳转测试(含 settle 校正与忙碌生命周期),以及 chat-scroll e2e 里的浏览器契约——在 88 轮 fixture 的尾部用键盘跳到未加载的第 1 轮,断言落点几何与导航栏渐变。
|
||||
@@ -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/api/session-controller/README.md
|
||||
README.md: 2f2192cde2941019c9687738f8e3f5e50ddd6d8e
|
||||
README.zh.md: 273d304d3cd3bbcbadd3e40202d8d923f3a27498
|
||||
README.md: 94e8697cd7f5bf75746edd5d691956e680d9b261
|
||||
README.zh.md: 0228ca15624413fcd5cb6e17afb8aaf0a28f780a
|
||||
@@ -27,7 +27,7 @@ History pages and follow opening snapshots carry a discriminated `SessionHistory
|
||||
|
||||
Each endpoint states its activation policy. List, search, attachment, history pages, log following, skill discovery, and workspace-path opening can inspect persistence without activating an Agent; `canOpenWorkspacePath()` reports native-opening availability without addressing a Session. Queue mutation and cancellation require live state; model, rename, prompt, and file-reference operations may resolve or resume an ordinary Session. Create and fork are the only operations that create a new Agent directly. The skill catalog instead uses a live Agent when present or the recorded preset's standing scope when cold, so listing never starts an Agent.
|
||||
|
||||
The Client adapter exposes `SessionEventStream`, a Gateway `RemoteJournalStream` bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous `replace`, `prepend`, and `append` changes, and repairs reconnect or sequence gaps through a tail page. Ordinary records cover `[event.seq, event.seq]`; packed rows cover `[event.seq, event.seq + memberCount - 1]`. A business, persistence, or unresolved continuity failure terminates the stream, while only physical carrier loss selects automatic resumption. `SessionControlStream` is a Gateway `RemoteSnapshotStream`; every generation opens with a complete process-local baseline, so reconnect replaces queue, jobs, and projection state instead of treating transient values as durable events.
|
||||
The Client adapter exposes `SessionEventStream`, a Gateway `RemoteJournalStream` bound to one ordinary or direct-subagent address. It opens follow before the initial page, publishes only contiguous `replace`, `prepend`, and `append` changes, and repairs reconnect or sequence gaps through a tail page. Backwards paging has two verbs: `loadOlder()` pulls one 50-message page, and `loadThrough(seq)` — the turn-jump loader — loops 200-message pages until the window covers the target seq, lowering a shared target on repeated calls, stopping on a page that makes no progress, and reporting busy through the same `loadingOlder` snapshot bit. Ordinary records cover `[event.seq, event.seq]`; packed rows cover `[event.seq, event.seq + memberCount - 1]`. A business, persistence, or unresolved continuity failure terminates the stream, while only physical carrier loss selects automatic resumption. `SessionControlStream` is a Gateway `RemoteSnapshotStream`; every generation opens with a complete process-local baseline, so reconnect replaces queue, jobs, and projection state instead of treating transient values as durable events.
|
||||
|
||||
The Session object also carries local submission echoes: `session.beginSubmission` inserts one into `SessionSnapshot.pendingSubmissions` synchronously, before the caller serializes and prompts, so a conversation UI can show the message on the submit click's own frame. Session derives each echo's `transcript`, `queued`, or `steering` placement from its current running state and the requested delivery mode, then retains that placement while serialization is in flight. The prompt's `requestId` is the correlation identity: the Host echoes it as the durable user source's `rpcId`, and queue occurrences project it as `SessionQueuedItem.rpcId`. An echo retires one animation frame after its durable event or queue occurrence is observed (the delay keeps it renderable until the replacement is ready), immediately when its identified prompt fails or is abandoned, and as failed on disposal; each retirement fires the registered `onRetire` callback exactly once. Echoes are Client memory only; reload and reconnect rebuild the conversation from durable events alone.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ kind: "package-reference"
|
||||
|
||||
每个 endpoint 都声明自己的激活策略。列表、搜索、附件、历史页、日志跟随、skill 发现和工作区路径打开可以在不激活 Agent 的情况下检查 persistence;`canOpenWorkspacePath()` 无需指定 Session 即可报告原生打开能力。queue 变更与取消要求 live 状态;模型、重命名、prompt 和文件引用操作可以解析或恢复普通 Session。只有 create 与 fork 会直接创建新 Agent。skill 目录则优先使用已有 live Agent,否则使用所记录 preset 的常驻 scope,因此列表查询绝不会启动 Agent。
|
||||
|
||||
Client adapter 提供 `SessionEventStream`,即绑定到一个普通 Session 或 direct subagent address 的 Gateway `RemoteJournalStream`。它在读取首个 page 前打开 follow,只发布连续的 `replace`、`prepend` 和 `append` 变更,并通过 tail page 修复重连或 seq 缺口。普通 record 覆盖 `[event.seq, event.seq]`,packed row 覆盖 `[event.seq, event.seq + memberCount - 1]`。业务、persistence 或无法恢复的连续性错误会终止 stream,只有物理载体断开才触发自动恢复。`SessionControlStream` 是 Gateway `RemoteSnapshotStream`;每代都以完整的进程本地 baseline 开始,因此重连会替换 queue、jobs 和 projection 状态,而不会把瞬态值当作 durable event。
|
||||
Client adapter 提供 `SessionEventStream`,即绑定到一个普通 Session 或 direct subagent address 的 Gateway `RemoteJournalStream`。它在读取首个 page 前打开 follow,只发布连续的 `replace`、`prepend` 和 `append` 变更,并通过 tail page 修复重连或 seq 缺口。向后分页有两个动词:`loadOlder()` 拉一页 50 条 message,而 `loadThrough(seq)`——轮次跳转加载器——按 200 条 message 一页循环拉取直到窗口覆盖目标 seq,重复调用会下调共享目标,遇到无进展的页即停止,忙碌状态复用同一个 `loadingOlder` 快照位。普通 record 覆盖 `[event.seq, event.seq]`,packed row 覆盖 `[event.seq, event.seq + memberCount - 1]`。业务、persistence 或无法恢复的连续性错误会终止 stream,只有物理载体断开才触发自动恢复。`SessionControlStream` 是 Gateway `RemoteSnapshotStream`;每代都以完整的进程本地 baseline 开始,因此重连会替换 queue、jobs 和 projection 状态,而不会把瞬态值当作 durable event。
|
||||
|
||||
Session 对象还承载本地提交回显:`session.beginSubmission` 在调用方序列化与 prompt 之前,同步把一条回显写入 `SessionSnapshot.pendingSubmissions`,会话 UI 因此能在点击提交的当帧显示消息。Session 根据当前运行状态与请求的投递模式推导每条回显的 `transcript`、`queued` 或 `steering` 位置,并在序列化期间保留该位置。prompt 的 `requestId` 是关联标识:Host 把它回显为 durable user source 的 `rpcId`,queue occurrence 也把它投影为 `SessionQueuedItem.rpcId`。回显在观察到其 durable event 或 queue occurrence 后延迟一个动画帧退休,该延迟保证替代内容就绪前回显仍可渲染;带标识的 prompt 失败或被放弃时立即退休,销毁时按 failed 退休;每次退休恰好触发一次注册的 `onRetire` 回调。回显只存在于 Client 内存;刷新与重连只从 durable event 重建会话。
|
||||
|
||||
|
||||
@@ -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-chat/README.md
|
||||
README.md: 1b24e6bb6825826a97a22a32f43d0210a2f4c6ad
|
||||
README.zh.md: 86596bc73ffd1b41867af6559739ab5426adb7b1
|
||||
README.md: 0df0ef77b81fcaa16a197633be04d6e6c0d09b71
|
||||
README.zh.md: b35443c6207776a18cd85602ffb968a539babbee
|
||||
@@ -63,7 +63,8 @@ None; Chat presentation does not assemble or mutate provider requests.
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- **The view reflects the loaded Session window** — older transcript nodes become available only after Session Controller loads the preceding event page. Turn navigation likewise represents only loaded Turns; loading an earlier page preserves existing Turn marks and redistributes the complete loaded set in a compact rail without an unloaded-history placeholder. Marks stay 10px apart until the loaded set exceeds the available height, then compress to fit.
|
||||
- **The transcript reflects the loaded Session window** — older transcript nodes become available only after Session Controller loads the preceding event page. Turn navigation is wider than the window: the rail merges the loaded Turns with the host `turnOutline` projection, so every started Turn gets a fixed-pitch mark (10px apart; a ladder taller than the frame scrolls inside it with gradient fades), and activating an unloaded mark pages history through the Turn's `turn/start` seq before landing on its row. Without the projection (assemblies not mounting `dsh-session-turn-outline`) the rail falls back to loaded Turns only.
|
||||
- **Unloaded marks preview the prompt only** — the outline carries no response text, so an unloaded Turn's hover preview shows its first prompt (or just the Turn number) until its events load.
|
||||
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -63,7 +63,8 @@ Chat 会在历史前插与 renderer 重新挂载时恢复语义锚点。读者
|
||||
|
||||
<a id="known-limitations-and-deferred-work"></a>
|
||||
|
||||
- **视图只反映已加载的 Session 窗口**——只有 Session Controller 加载前一页 event 后,更早的 transcript node 才会出现。轮次导航同样只表示已加载的 Turn;加载更早一页时,已有 Turn 刻度保持身份不变,完整的已加载集合在紧凑轨道中重新排布,不显示未加载历史占位。刻度默认相隔 10px,仅在已加载集合超过可用高度时压缩间距。
|
||||
- **transcript 只反映已加载的 Session 窗口**——只有 Session Controller 加载前一页 event 后,更早的 transcript node 才会出现。轮次导航比窗口更宽:轨道把已加载的 Turn 与宿主 `turnOutline` 投影合并,每个已开始的 Turn 都有固定间距刻度(相隔 10px;阶梯高于外框时在框内滚动并以渐变淡出标示可滚方向),激活未加载刻度会先把历史分页拉到该 Turn 的 `turn/start` seq 再落到它的行上。没有该投影时(未挂载 `dsh-session-turn-outline` 的装配),轨道回退到仅显示已加载 Turn。
|
||||
- **未加载刻度只预览提示词**——大纲不含回复文本,未加载 Turn 的悬浮预览在其事件载入前只显示首条提示词(或仅轮次号)。
|
||||
|
||||
|
||||
<a id="dev-note"></a>
|
||||
|
||||
@@ -559,7 +559,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'ISession',
|
||||
declaration: 'export interface ISession {\n readonly sessionId: SessionId;\n readonly projections: ProjectionsFace;\n beginSubmission(input: BeginSubmissionInput): SubmissionHandle;\n prompt(content: PromptContentPart[], mode: \'queue\' | \'steer\', signal?: AbortSignal, requestId?: SessionRequestId): Promise<RemoteResult<{\n accepted: true;\n }>>;\n readAttachment(attachmentId: AttachmentIdType): Promise<RemoteResult<{\n attachment: ImageAttachmentRef;\n data: Uint8Array;\n }>>;\n updateQueue(itemId: MessageId, action: QueueAction): Promise<RemoteResult<{\n accepted: true;\n }>>;\n cancel(): Promise<RemoteResult<{\n accepted: true;\n }>>;\n rename(title: string): Promise<RemoteResult<{\n title: string;\n seq: number;\n }>>;\n loadOlder(): Promise<void>;\n command(line: string): Promise<RemoteResult<{\n matched: boolean;\n }>>;\n}',
|
||||
declaration: 'export interface ISession {\n readonly sessionId: SessionId;\n readonly projections: ProjectionsFace;\n beginSubmission(input: BeginSubmissionInput): SubmissionHandle;\n prompt(content: PromptContentPart[], mode: \'queue\' | \'steer\', signal?: AbortSignal, requestId?: SessionRequestId): Promise<RemoteResult<{\n accepted: true;\n }>>;\n readAttachment(attachmentId: AttachmentIdType): Promise<RemoteResult<{\n attachment: ImageAttachmentRef;\n data: Uint8Array;\n }>>;\n updateQueue(itemId: MessageId, action: QueueAction): Promise<RemoteResult<{\n accepted: true;\n }>>;\n cancel(): Promise<RemoteResult<{\n accepted: true;\n }>>;\n rename(title: string): Promise<RemoteResult<{\n title: string;\n seq: number;\n }>>;\n loadOlder(): Promise<void>;\n loadThrough(seq: number): Promise<void>;\n command(line: string): Promise<RemoteResult<{\n matched: boolean;\n }>>;\n}',
|
||||
},
|
||||
{
|
||||
name: 'KeyPropsOf',
|
||||
|
||||
@@ -204,7 +204,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
],
|
||||
replaceRisk: 'none',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.assistant-actions\', () => ctx.slots.register(\n { name: \'conversation.chat.assistant-actions\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:202',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:204',
|
||||
},
|
||||
{
|
||||
key: 'conversation.chat.commandview',
|
||||
@@ -249,7 +249,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
occupants: [],
|
||||
replaceRisk: 'none',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.commandview\', () => ctx.slots.register(\n { name: \'conversation.chat.commandview\', key: \'<one key the owner dispatches>\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:190',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:192',
|
||||
},
|
||||
{
|
||||
key: 'conversation.chat.node',
|
||||
@@ -313,7 +313,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
],
|
||||
replaceRisk: 'shadows-shipped-ui',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.node\', () => ctx.slots.register(\n { name: \'conversation.chat.node\', key: \'<one key the owner dispatches>\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:171',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:173',
|
||||
},
|
||||
{
|
||||
key: 'conversation.chat.turnTail',
|
||||
@@ -358,7 +358,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
],
|
||||
replaceRisk: 'none',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.turnTail\', () => ctx.slots.register(\n { name: \'conversation.chat.turnTail\', select: owner => null },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:196',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:198',
|
||||
},
|
||||
{
|
||||
key: 'conversation.composer',
|
||||
@@ -537,7 +537,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
],
|
||||
replaceRisk: 'shadows-shipped-ui',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.details.tool\', () => ctx.slots.register(\n { name: \'conversation.details.tool\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:208',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:210',
|
||||
},
|
||||
{
|
||||
key: 'conversation.hero.agentPreset',
|
||||
@@ -1025,7 +1025,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
|
||||
],
|
||||
replaceRisk: 'shadows-shipped-ui',
|
||||
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.message.images\', () => ctx.slots.register(\n { name: \'conversation.message.images\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:184',
|
||||
source: 'packages/client/ui-chat/src/client/contract/slots.ts:186',
|
||||
},
|
||||
{
|
||||
key: 'conversation.session',
|
||||
|
||||
Reference in new issue
Block a user