@xiongtao.xt/tau-plugins 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.16.0
4
+
5
+ - 新增独立 `usePluginServices()`,由插件渲染运行时提供已有编译与转换服务;博客及无服务的宿主返回 undefined。
6
+ - 服务按当前窗口和工作区绑定取消与失效保护,静态编辑器组件无需调用 activate;原有 context.services 保持不变。
7
+ - 不修改 useSourceBlock、EditorKit、文档节点协议或编译接口;保留 TTS、上传、聊天工具展示及 TikZ 服务删除边界。
8
+
9
+ ## 0.15.0
10
+
11
+ - 新增 `context.chat.registerToolRenderer`,按本插件 MCP ID 与原始工具名异步登记 React 工具渲染器,激活成功后发布,停用、重载或切库时回收。
12
+ - 本地工具支持 `{ content, render: { version, data } }`,展示数据沿 pi 会话保存并支持历史回放;保留默认展示、宿主审批及失败/取消语义。
13
+ - 渲染器仅匹配绑定工具,组件接收独立执行快照;修复异常订阅中断回收及审批/重试恢复状态的终态收敛。
14
+ - 保留 0.13.0 的 TTS、资源上传接口及 0.14.0 移除 TikZ 服务的边界;UI 包无需升级。
15
+
3
16
  ## 0.14.0
4
17
 
5
18
  - 移除 `PluginServices.tikz` 和 `context.services.tikz.compile`。TikZ 编译、字体与文档块由独立插件 tau.tikz 提供,宿主不再保留编译实现或转发入口。
package/MCP.md CHANGED
@@ -84,6 +84,31 @@ context.mcp.register({
84
84
  - 旧实例、旧数据库和重连前的调用不能转交给新实例。不要缓存上下文到下一次启用。
85
85
  - Skill 可描述这些工具的用途;ctx.ai.chat/stream 仍是文本生成接口,不会因为注册 MCP 而自动获得工具调用能力。
86
86
 
87
+ ## 聊天工具自定义渲染
88
+
89
+ `context.chat.registerToolRenderer` 的类型契约自 SDK 0.15.0 提供,须搭配包含实现的客户端构建。旧客户端即使同为 0.59.0 也可能没有此能力,必须检查方法是否存在;只提高 SDK 类型版本不能替代客户端检查。
90
+
91
+ 在 `async activate(context)` 中,先通过 `context.mcp.register()` 声明工具,再注册组件并 await:
92
+
93
+ ```tsx
94
+ await context.chat.registerToolRenderer({
95
+ mcpId: "notes",
96
+ toolName: "read_note",
97
+ component: ({ execution, renderDefault }) =>
98
+ execution.status === "success" ? <div>{execution.output}</div> : renderDefault(),
99
+ });
100
+ ```
101
+
102
+ 这段代码放在异步激活函数内;`mcpId` 是本插件注册 MCP 时的 id,`toolName` 是其中的原始工具名,不拼接全局名称。可以绑定本地工具或本插件 transport 中的远端工具,不能绑定内置工具、其他插件或用户手动添加的服务器。返回 `Promise<Disposable>`,失败随 await 抛出;激活成功后才发布组件,停用或切库自动回收,无需再次 context.register。
103
+
104
+ 本地工具可返回 `{ content: string, render: { version: 1, data: JsonValue } }`;content 提供模型文本及默认展示,render.data 是独立的可持久化展示数据。组件参数为 `PluginToolRendererProps`(从 `@tau/plugins` 导入),包括 execution、isDark、parsedInput、inputParseError、data、dataVersion 和 renderDefault。展示版本为正整数,数据只接受有限 JSON;不支持的版本应调用 renderDefault。
105
+
106
+ transport 工具当前读取 execution.output,不提供标准 structuredContent 或 MCP Apps HTML 界面能力。宿主决定渲染路由,远端返回不能选择其他渲染器。等待审批继续使用宿主审批 UI,失败和取消显示通用工具行。
107
+
108
+ 关闭 MCP 或工具执行开关不会隐藏历史结果;停用插件时回到通用输入输出,再启用后恢复。组件不应在渲染或挂载时自动执行工具。使用现有 React 和 `@tau/ui`/主题样式,不导入内部聊天包。
109
+
110
+ 完整示例见仓库 `scripts/plugins/fixtures/chat-tool-renderer.tsx`。在宿主仓库执行 `node scripts/plugins/verify-chat-author.mjs`,会生成 SDK 包、在独立目录安装、类型检查并构建该示例 ZIP;不安装到用户目录,也不发布。产物和验收记录保存在 `output/plugin-chat-sdk/`。
111
+
87
112
  ## 验证
88
113
 
89
114
  检查类型并打包后,在包含此实现的客户端启用插件:确认两项 MCP 出现在现有页面和快捷面板;测试整项开关、逐工具开关、外部连接配置和重连,再测试停用、重新启用及重启后偏好保留。真实服务器连接与模型调用需使用自己的服务和已配置的模型。
package/SERVICES.md CHANGED
@@ -16,6 +16,12 @@
16
16
  | `ffmpeg.probe(file, options?)` | `{ name, data: Blob }` | `{ format, streams }` |
17
17
  | `ffmpeg.run(input, options?)` | `{ files, args, outputs }` | `{ files, log }` |
18
18
 
19
+ ## 在静态组件中获取服务
20
+
21
+ SDK 0.16.0 的 `usePluginServices()` 返回当前插件可用的同一组服务,类型为 `PluginServices | undefined`。服务由插件运行时提供,独立编辑窗口无需 activate;博客和未包裹的普通组件返回 undefined,组件应显示已保存结果或源码。它不属于源码块或 EditorKit,不要将服务存入模块全局变量。
22
+
23
+ 组件需用自己的 AbortSignal 取消过期或卸载的调用,宿主同时处理窗口与工作区失效。内容插件的启停按现有重启生效规则处理;写入启停意图不会提前拆掉当前窗口已生效的贡献。
24
+
19
25
  ## 取消与错误
20
26
 
21
27
  第二个参数均支持 `{ signal: AbortSignal }`。插件停用会自动取消本实例的任务,不必重复传入 `context.signal`。用户主动取消可使用单次任务的 `AbortController`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiongtao.xt/tau-plugins",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,24 @@
1
+ import type { ComponentType, ReactNode } from "react";
2
+ import type { ToolCallExecutionState } from "../../shared/src/chat-core/index";
3
+ import type { Disposable } from "./contract";
4
+ import type { JsonValue } from "./json";
5
+ export type { ToolCallExecutionState } from "../../shared/src/chat-core/index";
6
+ export interface PluginToolRendererProps {
7
+ execution: Readonly<ToolCallExecutionState>;
8
+ isDark: boolean;
9
+ parsedInput: unknown | null;
10
+ inputParseError?: string;
11
+ data: JsonValue | null;
12
+ dataVersion: number | null;
13
+ renderDefault(): ReactNode;
14
+ }
15
+ export interface PluginToolRendererDefinition {
16
+ mcpId: string;
17
+ toolName: string;
18
+ component: ComponentType<PluginToolRendererProps>;
19
+ }
20
+ export interface PluginChat {
21
+ /** 激活成功后发布,停用时自动回收;展示不受 MCP 执行开关影响。 */
22
+ registerToolRenderer(definition: PluginToolRendererDefinition): Promise<Disposable>;
23
+ }
24
+ export declare function snapshotToolRendererDefinition(definition: PluginToolRendererDefinition): PluginToolRendererDefinition;
@@ -13,6 +13,7 @@ import type { PluginTTS } from "./tts";
13
13
  import type { PluginSidebar } from "./sidebar";
14
14
  import type { PluginExcerpts, PluginMaterials } from "./excerpts";
15
15
  import type { PluginMCP } from "./mcp";
16
+ import type { PluginChat } from "./chat";
16
17
  import type { JsonValue } from "./json";
17
18
  export type { JsonValue } from "./json";
18
19
  export interface Disposable {
@@ -50,6 +51,7 @@ export interface PluginContext {
50
51
  readonly services: PluginServices;
51
52
  readonly ai: PluginAI;
52
53
  readonly mcp: PluginMCP;
54
+ readonly chat: PluginChat;
53
55
  /** 将订阅等资源交给宿主,在停用或激活失败时逆序回收。 */
54
56
  register(resource: Disposable): Disposable;
55
57
  }
@@ -9,8 +9,11 @@ export type { SourceBlockEditorProps, PluginRuntimeStyleCompiler } from "./sourc
9
9
  export * from "./sidebar";
10
10
  export type * from "./excerpts";
11
11
  export type * from "./services";
12
+ export { usePluginServices } from "./service-access";
12
13
  export type { PluginSkill } from "./skills";
13
14
  export type { PluginMCP, PluginMCPDefinition, PluginMCPTool, PluginMCPTransport } from "./mcp";
15
+ export type { PluginMCPToolResult } from "./mcp";
16
+ export type { PluginChat, PluginToolRendererDefinition, PluginToolRendererProps, ToolCallExecutionState, } from "./chat";
14
17
  export type { PluginPageDefinition, PluginPageHandle, PluginPageParams, PluginPageProps, PluginPages, } from "./pages";
15
18
  export declare function definePlugin(definition: Omit<PluginDefinition, "apiVersion" | "create"> & Partial<Pick<PluginDefinition, "create">>): PluginDefinition;
16
19
  export type { PluginAI, PluginAIFeature, PluginAIRequest, PluginAIResult, PluginAIEvent, } from "./ai";
@@ -11,6 +11,13 @@ export type PluginMCPTransport = {
11
11
  url: string;
12
12
  headers?: Record<string, string>;
13
13
  };
14
+ export interface PluginMCPToolResult {
15
+ content: string;
16
+ render?: {
17
+ version: number;
18
+ data: JsonValue;
19
+ };
20
+ }
14
21
  export interface PluginMCPTool {
15
22
  name: string;
16
23
  description: string;
@@ -21,11 +28,7 @@ export interface PluginMCPTool {
21
28
  };
22
29
  execute(args: Record<string, JsonValue>, context: {
23
30
  signal: AbortSignal;
24
- }): Promise<{
25
- content: string;
26
- }> | {
27
- content: string;
28
- };
31
+ }): Promise<PluginMCPToolResult> | PluginMCPToolResult;
29
32
  }
30
33
  export type PluginMCPDefinition = {
31
34
  id: string;
@@ -42,4 +45,5 @@ export interface PluginMCP {
42
45
  /** 同一插件可注册多个 MCP;停用时由宿主自动回收。 */
43
46
  register(definition: PluginMCPDefinition): Disposable;
44
47
  }
48
+ export declare function snapshotMCPToolResult(result: PluginMCPToolResult): PluginMCPToolResult;
45
49
  export declare function snapshotMCPDefinition(definition: PluginMCPDefinition): PluginMCPDefinition;
@@ -0,0 +1,7 @@
1
+ import type { PluginServices } from "./services";
2
+ declare let useServices: () => PluginServices | undefined;
3
+ /** 宿主绑定 React 上下文,作者只消费 usePluginServices。 */
4
+ export declare function configurePluginServiceAccess(read: typeof useServices): void;
5
+ /** 当前插件渲染作用域的服务;博客和无界面宿主不提供服务。 */
6
+ export declare function usePluginServices(): PluginServices | undefined;
7
+ export {};
@@ -0,0 +1,161 @@
1
+ /**
2
+ * 浏览器扩展 ↔ 桌面的 AI 运行协议 v2(M5)。取代 v1 的 `ai.chat` 请求 + `ai.stream.*` 快照通知。
3
+ *
4
+ * 一次运行 = 一个 runId:
5
+ * - `ai.run.start`(请求)立即返回 `{ runId }`,不等回答结束;
6
+ * - 过程经 `ai.run.event` 通知推送(按 runId 路由,`seq` 单调递增,接收端据此丢弃乱序/重复);
7
+ * - 最后一条一定是 `settled`(成功、失败、被停止都会有),接收端据此收尾;
8
+ * - `ai.run.abort` 停止;`ai.run.state` 查某个扩展对话此刻是否在跑(侧栏重开后接上)。
9
+ *
10
+ * 事件是快照语义,与 v1 相同的块组装(applyChatStreamEvent)可以直接消费 text/reasoning/tool-calls;
11
+ * v1 丢掉的 usage、重试、错误、交互(审批/提问)在 v2 里都有。
12
+ */
13
+ import type { ChatSessionMessage, ModelUsage, ResponseMessage, ToolCallExecutionState, ToolCallPayload } from "./messages";
14
+ /** 审批:模型要执行需要确认的工具 */
15
+ export interface AiRunApprovalRequest {
16
+ requestId: string;
17
+ toolCallId: string;
18
+ toolName: string;
19
+ risk: string;
20
+ input: string;
21
+ requestedAt: number;
22
+ }
23
+ /** 提问:宿主原生 ask-question,payload 为已归一化的问题结构 */
24
+ export interface AiRunQuestionRequest {
25
+ requestId: string;
26
+ toolCallId: string;
27
+ payload: unknown;
28
+ askedAt: number;
29
+ }
30
+ export type AiRunEvent = {
31
+ type: "text";
32
+ turnId: string;
33
+ fullText: string;
34
+ } | {
35
+ type: "reasoning";
36
+ turnId: string;
37
+ fullText: string;
38
+ } | {
39
+ type: "tool-calls";
40
+ turnId: string;
41
+ executions: ToolCallExecutionState[];
42
+ toolCalls?: ToolCallPayload[];
43
+ } | {
44
+ type: "turn-finish";
45
+ turnId: string;
46
+ } | {
47
+ type: "usage";
48
+ turnId: string;
49
+ usage: ModelUsage;
50
+ }
51
+ /** 自动重试中(null = 重试等待结束) */
52
+ | {
53
+ type: "retry";
54
+ status: {
55
+ attempt: number;
56
+ maxAttempts: number;
57
+ delayMs: number;
58
+ } | null;
59
+ }
60
+ /** 模型或运行报错(不一定终止:重试可能接着成功;终止与否看 settled) */
61
+ | {
62
+ type: "error";
63
+ message: string;
64
+ }
65
+ /** 待用户处理的交互快照(审批 + 提问),为空表示没有在等 */
66
+ | {
67
+ type: "interactions";
68
+ approvals: AiRunApprovalRequest[];
69
+ questions: AiRunQuestionRequest[];
70
+ }
71
+ /** 运行结束(唯一终态):ok=false 时带 error;aborted 表示用户停止 */
72
+ | {
73
+ type: "settled";
74
+ ok: boolean;
75
+ aborted: boolean;
76
+ error?: string;
77
+ lastText: string;
78
+ };
79
+ export interface AiRunEventNotification {
80
+ runId: string;
81
+ seq: number;
82
+ event: AiRunEvent;
83
+ }
84
+ export interface AiRunStartParams {
85
+ /** 客户端生成(接收端先订阅再发起,避免漏掉最早的事件) */
86
+ runId: string;
87
+ /**
88
+ * 持久对话(侧栏):只带本轮的用户消息,历史由桌面从页面会话文件恢复。
89
+ * 一次性调用(概览页、行内问答):带全量历史,最后一条为 user。
90
+ */
91
+ messages: Array<{
92
+ role: string;
93
+ content: unknown;
94
+ [key: string]: unknown;
95
+ }>;
96
+ session?: {
97
+ id: string;
98
+ pageUrl: string;
99
+ };
100
+ systemPrompt?: string;
101
+ pageContext?: {
102
+ url: string;
103
+ title: string;
104
+ content?: string;
105
+ };
106
+ /** false = 纯文本单发(行内问答) */
107
+ enableTools?: boolean;
108
+ /** 扩展本地执行的页面工具(OpenAI function 形状) */
109
+ browserTools?: Array<{
110
+ type: "function";
111
+ function: {
112
+ name: string;
113
+ description: string;
114
+ parameters: Record<string, unknown>;
115
+ };
116
+ }>;
117
+ thinkingLevel?: string;
118
+ feature?: string;
119
+ }
120
+ /** 一轮运行到此刻的快照(侧栏重开后接回):之后的事件按 `lastSeq` 续接 */
121
+ export interface AiRunSnapshot {
122
+ runId: string;
123
+ lastSeq: number;
124
+ /**
125
+ * 这一轮开始前的对话投影。接回时界面 = baseMessages + request + response;不用会话文件的当前投影,
126
+ * 因为本轮已完成的回答片段可能已落盘(与 response 重复),也可能还没落盘(新对话首轮 pi 不写文件)。
127
+ */
128
+ baseMessages: ChatSessionMessage[];
129
+ /** 这一轮的用户消息(与 start 时的 messages 末条同一形状) */
130
+ request: {
131
+ role: string;
132
+ content: unknown;
133
+ [key: string]: unknown;
134
+ };
135
+ /** 本轮回答到此刻的归约结果(与接收端同一套 applyChatStreamEvent 组装) */
136
+ response: ResponseMessage;
137
+ interactions: {
138
+ approvals: AiRunApprovalRequest[];
139
+ questions: AiRunQuestionRequest[];
140
+ };
141
+ retry: {
142
+ attempt: number;
143
+ maxAttempts: number;
144
+ delayMs: number;
145
+ } | null;
146
+ }
147
+ export interface AiRunStateResult {
148
+ running: boolean;
149
+ runId?: string;
150
+ snapshot?: AiRunSnapshot;
151
+ }
152
+ export declare const AI_RUN_METHODS: {
153
+ readonly start: "ai.run.start";
154
+ readonly abort: "ai.run.abort";
155
+ readonly state: "ai.run.state";
156
+ readonly approve: "ai.run.approve";
157
+ readonly answer: "ai.run.answer";
158
+ readonly event: "ai.run.event";
159
+ };
160
+ /** 按 runId 收事件:丢弃乱序与重复(seq 不大于已收到的);接回在跑的运行时从快照的 lastSeq 续接 */
161
+ export declare const createRunEventFilter: (runId: string, afterSeq?: number) => (notification: AiRunEventNotification) => AiRunEvent | null;
@@ -0,0 +1,12 @@
1
+ import type { RequestMessage, ProviderMetadata, ResponseContentBlock, ResponseMessage, ToolCallExecutionState, ToolCallPayload } from "./messages";
2
+ export declare const syncAssistantDerivedFields: (message: ResponseMessage) => ResponseMessage;
3
+ export declare const ensureAssistantMessage: (message: ResponseMessage) => ResponseMessage;
4
+ export declare const getAssistantToolExecutions: (message: ResponseMessage) => ToolCallExecutionState[];
5
+ export declare const getAssistantContentBlocks: (message: ResponseMessage) => ResponseContentBlock[];
6
+ export declare const createAssistantMessage: (initial?: Partial<ResponseMessage>) => ResponseMessage;
7
+ export declare const applyAssistantTextSnapshot: (message: ResponseMessage, turnId: string, fullText: string) => void;
8
+ export declare const applyAssistantReasoningSnapshot: (message: ResponseMessage, turnId: string, fullReasoning: string, now?: number) => void;
9
+ export declare const applyAssistantToolCallsSnapshot: (message: ResponseMessage, turnId: string, executions: ToolCallExecutionState[], toolCalls?: ToolCallPayload[]) => void;
10
+ export declare const finalizeAssistantTurn: (message: ResponseMessage, turnId: string) => void;
11
+ export declare const applyAssistantProviderMetadataSnapshot: (message: ResponseMessage, turnId: string, providerMetadata?: ProviderMetadata) => void;
12
+ export declare const buildAssistantHistoryMessages: (message: ResponseMessage) => RequestMessage[];
@@ -0,0 +1,6 @@
1
+ /** 不依赖 React 或 DOM 的公共聊天入口,可供主进程和浏览器共同使用。 */
2
+ export * from "./messages";
3
+ export * from "./messageSchema";
4
+ export * from "./assistantBlocks";
5
+ export * from "./streamEvents";
6
+ export * from "./aiRunProtocol";
@@ -0,0 +1 @@
1
+ export declare const createChatMessageClientId: () => string;
@@ -0,0 +1,195 @@
1
+ /**
2
+ * 统一消息模型:桌面端与浏览器插件共用的会话记录、请求/响应消息与流式块类型。
3
+ * 桌面 src/types/llm.ts、src/types/chat-message.ts、src/types/tool.ts 对本文件为 re-export 关系,
4
+ * 两端禁止再各自定义同名类型。
5
+ */
6
+ import type { MentionRef } from "../components/ChatInputArea/types";
7
+ export declare enum Role {
8
+ User = "user",
9
+ Assistant = "assistant",
10
+ System = "system",
11
+ Tool = "tool"
12
+ }
13
+ export interface TextContent {
14
+ type: "text";
15
+ text: string;
16
+ mentions?: MentionRef[];
17
+ /** 本条消息用 `$` 点名的 Skill(folderName),发送时注入全文(D-060) */
18
+ skills?: string[];
19
+ }
20
+ export interface ImageContent {
21
+ type: "image";
22
+ image: string;
23
+ path?: string;
24
+ resourceId?: number;
25
+ }
26
+ export interface FileContent {
27
+ type: "file";
28
+ file: string;
29
+ resourceId?: number;
30
+ fileName?: string;
31
+ }
32
+ export type MessageContent = TextContent | ImageContent | FileContent;
33
+ export type ProviderMetadata = Record<string, unknown>;
34
+ export interface ToolCallPayload {
35
+ id: string;
36
+ type?: string;
37
+ function: {
38
+ name: string;
39
+ arguments?: string;
40
+ };
41
+ }
42
+ export type ToolCallStatus = "pending" | "executing" | "success" | "failed" | "retrying" | "cancelled" | "awaiting-approval";
43
+ /** 工具调用执行状态详情 */
44
+ export interface ToolCallExecutionState {
45
+ toolCallId: string;
46
+ toolName: string;
47
+ status: ToolCallStatus;
48
+ input: string;
49
+ output?: string;
50
+ error?: string;
51
+ metadata?: Record<string, unknown>;
52
+ /** 错误类型:parameter 为参数错误(已反馈模型纠正),execution 为执行失败(已自动重试) */
53
+ errorType?: "parameter" | "execution";
54
+ /** 首次进入 pending 的时间戳 */
55
+ timestamp: number;
56
+ duration?: number;
57
+ /** 自动重试次数(仅用于执行失败的自动重试场景) */
58
+ retryCount?: number;
59
+ }
60
+ export type AssistantBlockState = "streaming" | "final";
61
+ export interface AssistantTextBlock {
62
+ id: string;
63
+ turnId: string;
64
+ type: "text";
65
+ state: AssistantBlockState;
66
+ content: string;
67
+ }
68
+ export interface AssistantReasoningBlock {
69
+ id: string;
70
+ turnId: string;
71
+ type: "reasoning";
72
+ state: AssistantBlockState;
73
+ content: string;
74
+ startTime?: number;
75
+ endTime?: number;
76
+ duration?: number;
77
+ }
78
+ export interface AssistantToolCallBlock {
79
+ id: string;
80
+ turnId: string;
81
+ type: "tool_call";
82
+ state: AssistantBlockState;
83
+ toolCall: ToolCallPayload;
84
+ execution?: ToolCallExecutionState;
85
+ }
86
+ /** 流式助手块:文本/思考/工具调用按 turn 顺序交替出现 */
87
+ export type AssistantBlock = AssistantTextBlock | AssistantReasoningBlock | AssistantToolCallBlock;
88
+ /** 内容块:ChatMessageType.contentBlocks 的展示层结构 */
89
+ export type ResponseContentBlock = {
90
+ type: "text";
91
+ content: string;
92
+ } | {
93
+ type: "reasoning";
94
+ content: string;
95
+ startTime?: number;
96
+ endTime?: number;
97
+ duration?: number;
98
+ } | {
99
+ type: "tool_calls";
100
+ executions: ToolCallExecutionState[];
101
+ };
102
+ /** ContentBlock 为 ResponseContentBlock 的兼容别名(历史导出名) */
103
+ export type ContentBlock = ResponseContentBlock;
104
+ export interface ModelUsage {
105
+ inputTokens: number;
106
+ outputTokens: number;
107
+ cacheReadTokens: number;
108
+ cacheWriteTokens: number;
109
+ providerId?: string;
110
+ modelId?: string;
111
+ modelName?: string;
112
+ }
113
+ export interface MessageVersions {
114
+ ids: string[];
115
+ index: number;
116
+ }
117
+ export interface RequestMessage {
118
+ clientId?: string;
119
+ /**
120
+ * 对应的 pi 会话 entry id(M4:从 JSONL 投影出来的消息才有)。改历史操作(删除/编辑重发/
121
+ * 重新生成)按它定位,不能靠 clientId——迁移数据的 clientId 是旧值。
122
+ */
123
+ piEntryIds?: string[];
124
+ /**
125
+ * 会话树上同一位置的全部版本(M4-d:编辑重发/重新生成/从这里建分支产生的兄弟)。
126
+ * ids 为各版本用户消息的 entry id(写入顺序),index 是本条是第几个;只有一个版本时不带。
127
+ */
128
+ piVersions?: MessageVersions;
129
+ role: Role;
130
+ content: MessageContent[] | string;
131
+ reasoning_content?: string;
132
+ provider_metadata?: ProviderMetadata;
133
+ name?: string;
134
+ tool_call_id?: string;
135
+ tool_calls?: ToolCallPayload[];
136
+ }
137
+ export interface ResponseMessage {
138
+ clientId?: string;
139
+ /** 一条回答的全部 pi entry id(assistant + toolResult):删除回答要全部省略,否则只删一半 */
140
+ piEntryIds?: string[];
141
+ role: Role;
142
+ content: string;
143
+ reasoning_content?: string;
144
+ provider_metadata_by_turn?: Record<string, ProviderMetadata>;
145
+ tool_calls?: ToolCallPayload[];
146
+ assistant_blocks?: AssistantBlock[];
147
+ usage?: ModelUsage;
148
+ }
149
+ export type ChatSessionMessage = RequestMessage | ResponseMessage;
150
+ /**
151
+ * 一个已打开的对话(M4:会话目录行 + pi JSONL 投影出的消息)。注意与 ChatComponents 的
152
+ * ChatMessage(单条消息)区分,桌面历史代码以 `ChatMessage` 名字消费本类型。
153
+ *
154
+ * id 即 pi 会话 id(字符串):新对话在渲染端生成 id,目录表里查不到就是草稿,
155
+ * 首轮发送后才登记目录行,id 从头到尾不变。
156
+ */
157
+ export interface ChatSessionRecord {
158
+ /** 桌面投影所属库,不属于消息内容。 */
159
+ databaseName?: string;
160
+ id: string;
161
+ createTime: number;
162
+ updateTime: number;
163
+ messages: ChatSessionMessage[];
164
+ title: string;
165
+ titleDirty?: boolean;
166
+ groupId?: number | null;
167
+ archived?: boolean;
168
+ orderNum?: number | null;
169
+ /** 会话来源:desktop 为桌面端创建,browser 为浏览器插件创建 */
170
+ source?: "desktop" | "browser";
171
+ /** 相对会话目录的 JSONL 文件名;草稿(尚未登记)为空 */
172
+ sessionFile?: string;
173
+ /** 聊天场景(main-chat / pdf-chat / …)与所属对象 */
174
+ scope?: string;
175
+ domainRef?: string | null;
176
+ }
177
+ export interface ChatGroup {
178
+ id: number;
179
+ name: string;
180
+ parentId: number | null;
181
+ orderIndex: number;
182
+ createTime: number;
183
+ updateTime: number;
184
+ }
185
+ export interface CreateChatGroup {
186
+ name: string;
187
+ parentId?: number | null;
188
+ orderIndex?: number;
189
+ }
190
+ export interface UpdateChatGroup {
191
+ id: number;
192
+ name?: string;
193
+ parentId?: number | null;
194
+ orderIndex?: number;
195
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 统一流事件与归约器:把「快照语义」的流事件应用到 ResponseMessage,
3
+ * 内部复用 assistantBlocks 的块组装逻辑。
4
+ * 浏览器扩展消费 AI 运行协议 v2 的 `ai.run.event`(aiRunProtocol.ts),桌面主进程的扩展桥用它维护
5
+ * 接回快照;两端禁止再各自手写流式拼块。
6
+ */
7
+ import type { ModelUsage, ProviderMetadata, ResponseMessage, ToolCallExecutionState, ToolCallPayload } from "./messages";
8
+ /** turn 内快照事件:fullText / executions 为当前全量,幂等可重放 */
9
+ export type ChatStreamTurnEvent = {
10
+ type: "text";
11
+ turnId: string;
12
+ fullText: string;
13
+ } | {
14
+ type: "reasoning";
15
+ turnId: string;
16
+ fullText: string;
17
+ } | {
18
+ type: "tool-calls";
19
+ turnId: string;
20
+ /** 执行状态快照;流式计划阶段可只带 toolCalls */
21
+ executions?: ToolCallExecutionState[];
22
+ toolCalls?: ToolCallPayload[];
23
+ } | {
24
+ type: "turn-finish";
25
+ turnId: string;
26
+ } | {
27
+ type: "provider-metadata";
28
+ turnId: string;
29
+ metadata: ProviderMetadata;
30
+ };
31
+ /** run 级事件 */
32
+ export type ChatStreamRunEvent = {
33
+ type: "usage";
34
+ turnId?: string;
35
+ usage: ModelUsage;
36
+ } | {
37
+ type: "run-finish";
38
+ content?: string;
39
+ reasoning?: string;
40
+ error?: string;
41
+ };
42
+ export type ChatStreamEvent = ChatStreamTurnEvent | ChatStreamRunEvent;
43
+ /**
44
+ * 把流事件应用到助手消息(原地更新并返回同一引用;调用方负责渲染投影时复制)。
45
+ * run-finish / usage 不改变块结构,仅原样返回。
46
+ */
47
+ export declare const applyChatStreamEvent: (message: ResponseMessage, event: ChatStreamEvent) => ResponseMessage;
@@ -0,0 +1,126 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * 模型下拉菜单项(antd Dropdown 时代的结构子集;ChatInputArea 换 @tau/ui Menu 后收敛为原语协议)。
4
+ * ext / desktop 侧构造的字段不超出此面,antd message 等 UI 库类型不得回流 shared 类型层。
5
+ */
6
+ export interface ChatDropdownItem {
7
+ key: string;
8
+ label: ReactNode;
9
+ /** 分组小标题(非交互项,如模型菜单的 provider 名) */
10
+ group?: boolean;
11
+ danger?: boolean;
12
+ disabled?: boolean;
13
+ icon?: ReactNode;
14
+ }
15
+ /**
16
+ * 思考档位——全仓唯一定义,取值与运行时(pi ThinkingLevel)逐项对齐。
17
+ * 桌面端 `PiThinkingLevel` 即本类型;此前两侧各有一套(旧枚举用 "none" 且没有 "minimal"),
18
+ * 导致 minimal 在界面上不可达、每处转换都要记得 none→off。
19
+ */
20
+ export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
21
+ export type MentionEntityType = "card" | "article" | "project-item";
22
+ /** 聊天 mention 引用 */
23
+ export interface MentionRef {
24
+ entityType: MentionEntityType;
25
+ entityId: number;
26
+ contentId: number;
27
+ title: string;
28
+ previewText: string;
29
+ }
30
+ /** 消息内容类型;resourceId 仅兼容历史数据,新附件只写入路径。 */
31
+ export type MessageContentItem = {
32
+ type: "text";
33
+ text: string;
34
+ mentions?: MentionRef[];
35
+ skills?: string[];
36
+ } | {
37
+ type: "image";
38
+ image: string;
39
+ path?: string;
40
+ resourceId?: number;
41
+ } | {
42
+ type: "file";
43
+ file: string;
44
+ resourceId?: number;
45
+ fileName?: string;
46
+ };
47
+ /** ChatInputArea 需要的服务函数 */
48
+ export interface ChatInputAreaServices {
49
+ /** 上传文件,返回文件路径 */
50
+ uploadFile: (file: File) => Promise<string | null>;
51
+ /** 将本地文件路径转换为可访问的 URL */
52
+ convertFileSrc: (path: string) => Promise<string>;
53
+ /** 获取用户主目录 */
54
+ getHomeDir: () => Promise<string>;
55
+ /** 搜索可提及的内部内容 */
56
+ searchMentionRefs?: (query: string) => Promise<MentionRef[]>;
57
+ }
58
+ /** ChatInputArea 的 ref handle */
59
+ export interface ChatInputAreaHandle {
60
+ getValue: () => MessageContentItem[];
61
+ clear: () => void;
62
+ setValue: (content: string) => void;
63
+ appendMention?: (mention: MentionRef) => void;
64
+ focusEnd: () => void;
65
+ }
66
+ export interface ChatContextUsage {
67
+ inputTokens: number;
68
+ outputTokens: number;
69
+ cacheReadTokens: number;
70
+ cacheWriteTokens: number;
71
+ }
72
+ /** ChatInputArea 的 props */
73
+ export interface ChatInputAreaProps {
74
+ /** 服务函数 */
75
+ services: ChatInputAreaServices;
76
+ /** 自定义类名 */
77
+ className?: string;
78
+ /** 是否可编辑 */
79
+ contentEditable?: boolean;
80
+ /** 按下回车时的回调 */
81
+ onPressEnter?: () => void;
82
+ /** 停止生成时的回调 */
83
+ onStop?: () => void;
84
+ /** 占位符 */
85
+ placeholder?: string;
86
+ /** 是否正在发送 */
87
+ sendLoading?: boolean;
88
+ /** 生成期间是否仍允许编辑并将消息加入发送队列 */
89
+ allowSendWhileLoading?: boolean;
90
+ /** 当前等待发送的消息数量 */
91
+ queuedMessageCount?: number;
92
+ /** 是否正在创建新消息 */
93
+ createMessageLoading?: boolean;
94
+ /** 创建新消息的回调 */
95
+ onCreateNewMessage?: () => void;
96
+ /** 模型选择菜单项 */
97
+ modelSelectItems?: ChatDropdownItem[];
98
+ /** 当前选中模型的菜单项 key,用于菜单勾选 */
99
+ currentModelKey?: string;
100
+ /** 模型选择回调 */
101
+ onModelSelect?: ({ key }: {
102
+ key: string;
103
+ }) => void;
104
+ /** 当前模型名称 */
105
+ currentModelName?: string;
106
+ /** 当前模型上下文窗口,单位为 k tokens */
107
+ contextWindowK?: number;
108
+ /** 当前聊天最近一次模型请求的 token 用量 */
109
+ contextUsage?: ChatContextUsage | null;
110
+ /** 是否支持图片上传 */
111
+ isSupportImage?: boolean;
112
+ /** 是否支持文件上传 */
113
+ isSupportFile?: boolean;
114
+ /** 思考档位 */
115
+ thinkingLevel?: ThinkingLevel;
116
+ /** 当前模型真实支持的档位(由模型能力决定,不是固定几档) */
117
+ thinkingLevelOptions?: readonly ThinkingLevel[];
118
+ /** 档位变化回调 */
119
+ onThinkingLevelChange?: (level: ThinkingLevel) => void;
120
+ /** 档位选择是否禁用 */
121
+ thinkingLevelDisabled?: boolean;
122
+ /** MCP 管理按钮点击回调(可选,不提供则不显示按钮) */
123
+ onMcpManagerClick?: () => void;
124
+ /** 额外的工具栏按钮(渲染在 + 右侧) */
125
+ extraToolbarButtons?: ReactNode;
126
+ }