Merge remote-tracking branch 'origin/master' into feat/add-session-data-preview

# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/session.i18n.yaml
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/llm/token-meter/README.i18n.yaml
This commit is contained in:
Yichen Jiang
2026-08-05 15:51:34 +08:00
1464 changed files with 14404 additions and 5024 deletions
@@ -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-conversation/README.md
README.md: 737a53da1fa4541f81d89b41945cacd2b415ef4a
README.zh.md: 800d2b3e762d410100c8c9eb64bfbcdb22cda62a
README.md: 6c3cbd6ddc83001561b9cac24be33ed99fb7e6df
README.zh.md: 46cedbbd04e1fcb4071f736a80001cbadb643442
+1 -1
View File
@@ -10,7 +10,7 @@ The resident conversation shell survives no-session and session transitions. Wit
The view ring IS a slot: the conversation registration declares the `'conversation.view'` list slot (session scope) in its `children` table, ConversationRoot renders the active entry through its renderSlot share (`only: <active id>`), and view tabs project from the ring ledger's registration options (`id`/`order`/`label`). The chat view is this package's own ring entry; other plugins (ui-trajectory) contribute tabs through plain `ctx.slots.register` — the former package-local view registry (`registerView`/`ViewEntry`/`ConversationViewMap` and the chrome attachment table) is retired, with per-view chrome dissolved into the view components themselves.
Approvals take over the composer through the chain this package declares: `ApprovalPanel` registers as a selector-routed `'conversation.composer'` entry (the ui-question pattern) and occupies the composer in place of the InputBar while an approval wait is pending (amber strip, justification headline, paired command line from the running call's args, one-shot refuse/allow). The `PendingApproval` domain face in `contract/slots.ts` owns the wire encoding — the `ApprovalResponsePayload` value with the audit correlation — over the runtime's `PendingWait` carrier; the broadcast `approval/resolved` frame settles the wait and restores the composer. The runtime manager tracks this approval wait through the `waitingApproval` list bit even for uninstantiated sessions; `ui-workspace` owns its sidebar presentation. Pending waits leave the message flow entirely: questions (ui-question) and approvals (ApprovalPanel) both answer through the composer takeover, so no display-only placeholder card remains. The composer's bottom-row Access seat mounts `PermissionSelect`, fed by the host-computed `permissions` projection through the standard-kit `useProjection` (key absence hides the chip); the chip opens a Menu-primitive dropdown whose kebab-case preset names render as title-case labels. Safe preset picks submit `/permission <preset>` immediately through the bar's injected `command` callback, while `danger-full-access` is presented as `Full access` and first opens an in-page Modal risk confirmation. The enabling action stays disabled until the user checks the acknowledgement; cancel, Escape, close, and mask click submit nothing.
Approvals take over the composer through the chain this package declares: `ApprovalPanel` registers as a selector-routed `'conversation.composer'` entry (the ui-question pattern) and occupies the composer in place of the InputBar while an approval wait is pending (amber strip, justification headline, paired command line from the running call's args, one-shot refuse/allow). The `PendingApproval` domain face in `contract/slots.ts` owns the wire encoding — the `ApprovalResponsePayload` value with the audit correlation — over the runtime's `PendingWait` carrier; the broadcast `approval/resolved` frame settles the wait and restores the composer. The runtime manager projects every approval or question wait through `SessionSummary.pendingInteraction`, including sessions never instantiated; `ui-workspace` owns its sidebar presentation. Pending waits leave the message flow entirely: questions (ui-question) and approvals (ApprovalPanel) both answer through the composer takeover, so no display-only placeholder card remains. The composer's bottom-row Access seat mounts `PermissionSelect`, fed by the host-computed `permissions` projection through the standard-kit `useProjection` (key absence hides the chip); the chip opens a Menu-primitive dropdown whose kebab-case preset names render as title-case labels. Safe preset picks submit `/permission <preset>` immediately through the bar's injected `command` callback, while `danger-full-access` is presented as `Full access` and first opens an in-page Modal risk confirmation. The enabling action stays disabled until the user checks the acknowledgement; cancel, Escape, close, and mask click submit nothing.
The session header declares and renders the session-scoped `'conversation.session.header.actions'` list beside the title, allowing feature plugins to contribute controls without entering the skeleton. The composer chain currency includes the current conversation `session`; ui-subagent selects one-shot or parent-unavailable addressed sessions for reason-specific read-only copy, while the ordinary InputBar keeps every addressed child Send-only because the continuation service exposes no public per-Activation cancellation operation and `session.cancel` would bypass its ownership.
+1 -1
View File
@@ -32,7 +32,7 @@ Think 行默认保持折叠,并在不展开思维链的情况下暴露实时
工具行同样是 slot:独立工具环(`ToolViewRegistry``ctx.toolviews`/outlet)已经退役。聊天配置项声明键控的 `'conversation.chat.toolview'` 空位(Session scope;key 空间在运行时开放);其渲染点逐行通过 `entryKey: toolName` 分发,并以 `GenericToolCard` 作为调用点 `fallback`。owner 载荷是统一的 `ToolRowOwnerProps``callId``toolName``block``openFile`),`ToolRowProps` 则预先将其与 Session 标准工具包组合。注册方只是普通插件:`ctx.slots.register({ name: 'conversation.chat.toolview', key: '<tool>', inject? }, Row)`,以 `inject: ['slots', 'conversation']` 作为加载顺序 seam(apply 在聊天注册后挂载 ConversationService,因此服务存在即可保证 slot 已声明);bash 示例是第三方姿态的范例。Trajectory/waterfall(瀑布式事件)工具视图 slot 共享此形状,并随各自的渲染点落地(RendersCheck 会拒绝没有任何渲染方的声明)。
审批经由本包声明的链接管编辑器:`ApprovalPanel` 注册为按选择器路由的 `'conversation.composer'` 配置项(ui-question 模式),在审批等待未决期间取代 InputBar 占据编辑器(琥珀色条、理由标题、来自运行中调用参数的配对命令行、一次性的拒绝/允许)。`contract/slots.ts` 中的 `PendingApproval` 领域面在运行时 `PendingWait` 载体之上拥有 wire 编码——带审计关联的 `ApprovalResponsePayload` 值;广播的 `approval/resolved` 帧使等待落定并恢复编辑器。运行时 manager 通过 `waitingApproval` 列表位跟踪这种审批等待,未实例化的 Session 也不例外;`ui-workspace` 负责其侧边栏呈现。未决等待完全离开消息流:问题(ui-question)与审批(ApprovalPanel)都经编辑器接管作答,不再保留只读占位卡。编辑器底行的 Access 席位挂载 `PermissionSelect`,由 host 计算的 `permissions` 投影经标准工具包 `useProjection` 供数(key 缺席即隐藏 chip);chip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission <preset>`,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用;取消、Escape、关闭按钮与点击遮罩都不会提交命令。
审批经由本包声明的链接管编辑器:`ApprovalPanel` 注册为按选择器路由的 `'conversation.composer'` 配置项(ui-question 模式),在审批等待未决期间取代 InputBar 占据编辑器(琥珀色条、理由标题、来自运行中调用参数的配对命令行、一次性的拒绝/允许)。`contract/slots.ts` 中的 `PendingApproval` 领域面在运行时 `PendingWait` 载体之上拥有 wire 编码——带审计关联的 `ApprovalResponsePayload` 值;广播的 `approval/resolved` 帧使等待落定并恢复编辑器。运行时 manager 会将所有审批或问题等待通过 `SessionSummary.pendingInteraction` 投影出来,未实例化的 Session 也不例外;`ui-workspace` 负责其侧边栏呈现。未决等待完全离开消息流:问题(ui-question)与审批(ApprovalPanel)都经编辑器接管作答,不再保留只读占位卡。编辑器底行的 Access 席位挂载 `PermissionSelect`,由 host 计算的 `permissions` 投影经标准工具包 `useProjection` 供数(key 缺席即隐藏 chip);chip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission <preset>`,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用;取消、Escape、关闭按钮与点击遮罩都不会提交命令。
todo 两个面就是在该形状上的两个注册项,都是普通注册方插件,`inject: ['slots', 'conversation']``TodoRow` 占用 `'conversation.chat.toolview'``todo_write` key,摘要该次调用「试图写入」的内容(从其 args 解析出 `<已完成>/<总数> 已完成 · <进行中条目>`;模型 JSON 残缺或形状不对时回落到通用摘要;非 ok 执行状态保留通用状态点,使被取消的调用绝不读成一次已完成的更新)。`TodoDock``order: 0` 占用 `'conversation.input.dock'` 列表 slot(位于 Goal 与 Queue 之前),是计划条:它经 `useProjection` 读取 host 计算的 `todos` 投影(站立计划:其后没有更晚 `turn/start` 的最近一次 `todo/write`)并渲染 `TodoPanel`,后者接收纯列表,在列表为空时自我隐藏;列表非空时面板初始折叠,表头显示标题加 `"<已完成>/<总数> tasks · <n> in progress"`(状态图标为 figma 的勾选/进行中/虚线未开始一组)。选取由 dock 适配器负责,因此面板保持为其 props 的纯函数;站立列表放在此处而非行内,行才能保持单行。输入区 composer 链隐藏的一切(例如 ui-question 对 `conversation.composer` 的接管)也会隐藏整个 dock,包括这条计划条。
+1 -3
View File
@@ -72,8 +72,6 @@
"lib/index.js",
"lib/invariant.js",
"lib/client.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
"lib/types/**/*.d.ts"
]
}
@@ -90,7 +90,7 @@ async function bench(snapshot: ConversationSnapshot) {
const session = createSnapshotStore<ConversationSnapshot>(snapshot)
const list = createSnapshotStore<SessionListState>({
ids: [SID],
byId: { [SID]: { id: SID, title: 'S', displayTitle: 'S', running: false, waitingApproval: false, blank: false, updatedAt: 1 } },
byId: { [SID]: { id: SID, title: 'S', displayTitle: 'S', running: false, blank: false, updatedAt: 1 } },
current: SID,
phase: 'ready', subagentsByParent: {}, currentAddress: undefined,
})
@@ -292,7 +292,7 @@ describe('bash sample row', () => {
return createSnapshotStore<SessionListState>({
ids: [SID],
byId: {
[SID]: { id: SID, title: 'r', displayTitle: 'r', running: false, waitingApproval: false, blank: false, updatedAt: 0 },
[SID]: { id: SID, title: 'r', displayTitle: 'r', running: false, blank: false, updatedAt: 0 },
},
current: undefined,
phase: 'ready',
@@ -93,7 +93,7 @@ describe('tails', () => {
const sid = 'root-1' as SessionId
const list = createSnapshotStore<SessionListState>({
ids: [sid],
byId: { [sid]: { id: sid, title: 'r', displayTitle: 'r', running: false, waitingApproval: false, blank: false, updatedAt: 0 } },
byId: { [sid]: { id: sid, title: 'r', displayTitle: 'r', running: false, blank: false, updatedAt: 0 } },
current: undefined,
phase: 'ready',
subagentsByParent: {},
@@ -153,7 +153,7 @@ describe('chat row diff body', () => {
describe('FileMutationRow diff card', () => {
const list = () => createSnapshotStore<SessionListState>({
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0, cwd: '/w/app' } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0, cwd: '/w/app' } },
current: SID,
phase: 'ready',
subagentsByParent: {},
@@ -306,7 +306,7 @@ describe('DetailsPanel diff Output section', () => {
? { ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, currentAddress: undefined }
: {
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0, cwd } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0, cwd } },
current: SID,
phase: 'ready',
subagentsByParent: {},
@@ -167,7 +167,7 @@ describe('GenericToolCard read body', () => {
describe('ReadRow keyed toolview', () => {
const list = () => createSnapshotStore<SessionListState>({
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0, cwd: '/w/app' } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0, cwd: '/w/app' } },
current: SID,
phase: 'ready',
subagentsByParent: {},
@@ -254,7 +254,7 @@ describe('DetailsPanel Output section (read)', () => {
? { ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, currentAddress: undefined }
: {
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0, cwd } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0, cwd } },
current: SID,
phase: 'ready',
subagentsByParent: {},
@@ -92,10 +92,10 @@ function mount(
} = {},
) {
const root = sid('root')
const rootRow = { id: root, displayTitle: 'Root', running: false, waitingApproval: false, blank: false, updatedAt: 1 }
const rootRow = { id: root, displayTitle: 'Root', running: false, blank: false, updatedAt: 1 }
const childRow = {
id: SID, displayTitle: 'Child', parentId: root, cwd: '/projects/one',
running: false, waitingApproval: false, blank: options.summaryBlank ?? false, updatedAt: 2,
running: false, blank: options.summaryBlank ?? false, updatedAt: 2,
...(options.summaryOrigin === undefined ? {} : { origin: options.summaryOrigin }),
}
const listed = options.omitSummaryRow !== true
@@ -342,7 +342,7 @@ describe('chat row terminal body', () => {
describe('BashRow terminal card', () => {
const list = () => createSnapshotStore<SessionListState>({
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0 } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0 } },
current: undefined,
phase: 'ready',
subagentsByParent: {},
@@ -448,7 +448,7 @@ describe('DetailsPanel Output section', () => {
? { ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, currentAddress: undefined }
: {
ids: [SID],
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, waitingApproval: false, updatedAt: 0, cwd } },
byId: { [SID]: { id: SID, displayTitle: 'r', running: false, blank: false, updatedAt: 0, cwd } },
current: SID,
phase: 'ready',
subagentsByParent: {},