@plumeai/plugin-sdk 0.2.1 → 0.4.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/dist/client/ui/src/avatar/AgentAvatar.d.ts +2 -5
- package/dist/client/ui/src/i18n/messages/en.d.ts +64 -0
- package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +43 -0
- package/dist/client/ui/src/image/AnnotateToolbar.d.ts +2 -4
- package/dist/client/ui/src/image/Annotator.d.ts +3 -2
- package/dist/client/ui/src/image/screenshot-geometry.d.ts +2 -2
- package/dist/client/ui/src/inputs/SearchField.d.ts +1 -1
- package/dist/client/ui/src/inputs/pickers.d.ts +1 -1
- package/dist/client/ui/src/layout/PageTopRow.d.ts +5 -6
- package/dist/client/ui/src/motion/RollingText.d.ts +1 -1
- package/dist/client/ui/src/motion/loaders.d.ts +4 -4
- package/dist/client/ui/src/overlay/Confirm.d.ts +1 -2
- package/dist/client/ui/src/overlay/ContextMenu.d.ts +2 -2
- package/dist/client/ui/src/overlay/Dialog.d.ts +5 -6
- package/dist/client/ui/src/overlay/Menu.d.ts +3 -4
- package/dist/client/ui/src/overlay/Popover.d.ts +1 -1
- package/dist/client/ui/src/overlay/Tooltip.d.ts +1 -1
- package/dist/client/ui/src/primitives/IconButton.d.ts +2 -2
- package/dist/client/ui/src/primitives/Text.d.ts +2 -2
- package/dist/client/ui/src/scroll/ScrollText.d.ts +1 -1
- package/dist/client/ui/src/scroll/Scroller.d.ts +2 -1
- package/dist/contract/src/diagnostics.d.ts +39 -1
- package/dist/contract/src/service.d.ts +15 -2
- package/dist/contract/src/ui.d.ts +72 -68
- package/package.json +1 -1
- package/skill/SKILL.md +86 -79
- package/skill/references/components.md +233 -1418
- package/skill/references/logic.md +240 -75
- package/skill/references/style-i18n.md +16 -12
- package/skill/references/ui.md +170 -123
|
@@ -5,7 +5,7 @@ import type { Remote } from "../../infra/rpc/src/index.d.ts";
|
|
|
5
5
|
import type { ComponentType, MouseEvent, ReactNode } from "react";
|
|
6
6
|
import type { ChangeHunk } from "./changes.ts";
|
|
7
7
|
import type { Locale, SlashCommand, ToolCall, ToolOutput } from "./plugin.ts";
|
|
8
|
-
import type {
|
|
8
|
+
import type { ChildTask, FileContents, HumanRequest, OutgoingMessage, PermissionMode, SessionSummary, SessionTarget, SessionView, ThinkingLevel } from "./service.ts";
|
|
9
9
|
import type { AssistantMessage, DeliveredReport, UserMessage } from "../../core/llm/types.d.ts";
|
|
10
10
|
export type { Remote, SessionView, ToolCall, ToolOutput };
|
|
11
11
|
/** 回复正文中的围栏代码块;语法与内容由插件校验,宿主不执行其中的代码。 */
|
|
@@ -357,36 +357,10 @@ export interface ChildTasksProps {
|
|
|
357
357
|
/** 收了场的那件从列表里拿掉,见 `AgentService.dismissTask`。 */
|
|
358
358
|
dismiss(taskId: string): void;
|
|
359
359
|
}
|
|
360
|
-
/**
|
|
361
|
-
* 插件给自己的工具画的审批卡(`ui.approvalCard`)。卡片压在输入框正上方的那一格由产品摆,排队、停下这一轮时撤卡也归产品;
|
|
362
|
-
* 组件画整张卡(外框、正文、按钮),人拍板时调 `respond`。要人给答案(提问、计划、表单)的不是审批,用 `run.request` 和
|
|
363
|
-
* `ui.requestCard`(`RequestCardProps`)。
|
|
364
|
-
*/
|
|
365
|
-
export interface ApprovalCardProps {
|
|
366
|
-
request: ApprovalRequest;
|
|
367
|
-
/** 在问的会话;子会话(侧边聊天、子 Agent)问时是子会话的 id。 */
|
|
368
|
-
sessionId: string;
|
|
369
|
-
/** 问的是子会话时它的标题,卡上要写明是谁在请求。 */
|
|
370
|
-
from?: string;
|
|
371
|
-
/** 排在这张后面还在等的审批张数,卡上写 +N(`@plume/ui` 的 `ApprovalFrame` 替你写)。到期时刻在 `request.expiresAt`。 */
|
|
372
|
-
queued: number;
|
|
373
|
-
/** 交出回答。没送到时抛错,卡片自己把错误摆出来、让人再答。 */
|
|
374
|
-
respond(decision: ApprovalDecision): Promise<void>;
|
|
375
|
-
/** 设置里现在的权限模式。 */
|
|
376
|
-
permission: PermissionMode;
|
|
377
|
-
/**
|
|
378
|
-
* 把权限模式从请求批准切到帮我批准,给「接受并开启自动模式」这样的按钮:先切,再 `respond`。
|
|
379
|
-
* 只在人点了卡上的按钮时调;不在请求批准档时什么都不改(不会把完全访问降下来)。
|
|
380
|
-
*/
|
|
381
|
-
allowAuto(): Promise<void>;
|
|
382
|
-
/** 窄窗口:面板区一次只摆一格,打开面板会盖住这张卡,要读的东西画在卡里。 */
|
|
383
|
-
compact: boolean;
|
|
384
|
-
/** 产品的通用审批卡。认不出这次请求时直接返回它。 */
|
|
385
|
-
fallback: ReactNode;
|
|
386
|
-
}
|
|
387
360
|
/**
|
|
388
361
|
* 插件给 `run.request` 画的卡(`ui.requestCard`):问题、计划、表单。摆在哪、排队、过期、停下这一轮时撤卡归产品,
|
|
389
362
|
* 组件画整张卡(外框用 `@plume/ui` 的 `ApprovalFrame`),人答了调 `answer`,不答调 `decline`。
|
|
363
|
+
* 审批(放不放行)的卡只有产品的那一张,插件换不掉。
|
|
390
364
|
*/
|
|
391
365
|
export interface RequestCardProps {
|
|
392
366
|
request: HumanRequest;
|
|
@@ -402,7 +376,10 @@ export interface RequestCardProps {
|
|
|
402
376
|
decline(): Promise<void>;
|
|
403
377
|
/** 设置里现在的权限模式。 */
|
|
404
378
|
permission: PermissionMode;
|
|
405
|
-
/**
|
|
379
|
+
/**
|
|
380
|
+
* 把权限模式从请求批准切到帮我批准,给「接受并开启自动模式」这样的按钮:先调它,再 `answer`。
|
|
381
|
+
* 只在人点了卡上的按钮时调;不在请求批准档时什么都不改(不会把完全访问降下来)。
|
|
382
|
+
*/
|
|
406
383
|
allowAuto(): Promise<void>;
|
|
407
384
|
/** 窄窗口:面板区一次只摆一格,打开面板会盖住这张卡,要读的东西画在卡里。 */
|
|
408
385
|
compact: boolean;
|
|
@@ -786,15 +763,34 @@ export interface DraftQuote {
|
|
|
786
763
|
text: string;
|
|
787
764
|
from: "user" | "assistant";
|
|
788
765
|
}
|
|
766
|
+
/**
|
|
767
|
+
* 人在转录里划选一段字后,引用菜单里多的一项,见 `UiContext.quoteAction`。宿主只在人点了那一项时调 `run`;
|
|
768
|
+
* `quote` 是划选的那段,`sessionId` 是它所在的会话。拿去哪里由插件定(`ui.quoteInPanel` 放进自己面板的输入框)。
|
|
769
|
+
*/
|
|
789
770
|
export interface QuoteAction {
|
|
790
771
|
label: LocalizedText;
|
|
791
772
|
run(quote: DraftQuote, sessionId: string): void | Promise<void>;
|
|
792
773
|
}
|
|
774
|
+
/** `ui.onSessionsChanged` 收到的一次变化:只说哪个会话、哪一类变了,内容要自己重读。 */
|
|
793
775
|
export interface SessionChange {
|
|
794
776
|
sessionId: string;
|
|
777
|
+
/** `messages`:消息落盘或撤回;`summary`:摘要(标题、运行状态……)变了,新会话也算;`deleted`:删掉了。 */
|
|
795
778
|
type: "messages" | "summary" | "deleted";
|
|
779
|
+
/** 落盘的是工具结果时带上那件工具的名字,别的消息不带。 */
|
|
796
780
|
tools?: string[];
|
|
797
781
|
}
|
|
782
|
+
/** `ui.ComposerSend` 收的。 */
|
|
783
|
+
export interface ComposerSendProps {
|
|
784
|
+
/** 停止键停的会话。 */
|
|
785
|
+
sessionId: string;
|
|
786
|
+
/** 这个会话正在跑:画成停止键,按下停下它这一轮。 */
|
|
787
|
+
running: boolean;
|
|
788
|
+
/** 只禁用发送;跑着时停止键一直能按。 */
|
|
789
|
+
disabled?: boolean;
|
|
790
|
+
/** 发送键的悬停说明,不给是「发送」。 */
|
|
791
|
+
tip?: string;
|
|
792
|
+
onSend(): void;
|
|
793
|
+
}
|
|
798
794
|
/** 欢迎页问候下面那一行(`ui.welcomeLine`):一句话,可以带出处。 */
|
|
799
795
|
export interface WelcomeLine {
|
|
800
796
|
text: string;
|
|
@@ -807,34 +803,18 @@ export interface ComposerMention {
|
|
|
807
803
|
title: string;
|
|
808
804
|
description?: string;
|
|
809
805
|
origin?: string;
|
|
810
|
-
|
|
806
|
+
/**
|
|
807
|
+
* 宿主怎么画它:`agent` 是一个能点名叫来干活的智能体,归进「智能体」一组,行首和输入框里的 token 画它的头像(`avatar`);
|
|
808
|
+
* `plugin` 归进「插件和技能」一组,行首是插件图标。只管画法和分组,选中照样只插入 `token`。
|
|
809
|
+
*/
|
|
810
|
+
kind: "agent" | "plugin";
|
|
811
811
|
token: string;
|
|
812
|
+
/** 只给 `agent`:写明的头像,写法同 `@plume/ui` 的 `assignAvatars` 收的 `avatar`;不给按名字算一个,同一份名单里互不相同。 */
|
|
812
813
|
avatar?: string;
|
|
813
814
|
}
|
|
814
815
|
export interface UiContext {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
quoteInPanel(panelId: string, sessionId: string, quote: DraftQuote): void;
|
|
818
|
-
/** 仅接受当前真实点击,与主输入框的停止相同。 */
|
|
819
|
-
stopSession(sessionId: string, event: MouseEvent): Promise<void>;
|
|
820
|
-
onSessionsChanged(listener: (event: SessionChange) => void): () => void;
|
|
821
|
-
openMediaFile(name: string, sessionId: string): Promise<void>;
|
|
822
|
-
revealMediaFile(name: string): Promise<void>;
|
|
823
|
-
revealMediaLabel(): string;
|
|
824
|
-
ComposerSend: ComponentType<{
|
|
825
|
-
running: boolean;
|
|
826
|
-
disabled?: boolean;
|
|
827
|
-
tip?: string;
|
|
828
|
-
onSend(): void;
|
|
829
|
-
onStop(event: MouseEvent<HTMLButtonElement>): void;
|
|
830
|
-
}>;
|
|
831
|
-
/** 文件树等入口打开终端;处理器只在用户操作时运行。 */
|
|
832
|
-
terminalOpener(open: (directory: string, sessionId: string) => void | Promise<void>): void;
|
|
833
|
-
/**
|
|
834
|
-
* 回复里 shell 代码块的「在终端运行」:人点击后调用 `run(command, sessionId)`,`command` 已去掉提示符(`$ `、`> `)和注释行,
|
|
835
|
-
* 要原样执行;`sessionId` 是代码块所在的会话。登记和没有处理器时的提示同 `terminalOpener`。
|
|
836
|
-
*/
|
|
837
|
-
terminalRunner(run: (command: string, sessionId: string) => void | Promise<void>): void;
|
|
816
|
+
/** 插件名,和逻辑入口的一样。 */
|
|
817
|
+
readonly name: string;
|
|
838
818
|
/** 会话标题栏的局部控件;只在有会话时显示,组件不能接管标题或布局。 */
|
|
839
819
|
sessionToolbar(id: string, component: ComponentType<{
|
|
840
820
|
session: SessionView;
|
|
@@ -867,7 +847,6 @@ export interface UiContext {
|
|
|
867
847
|
readonly ProjectOpener: ComponentType<{
|
|
868
848
|
path: string;
|
|
869
849
|
}>;
|
|
870
|
-
readonly name: string;
|
|
871
850
|
/**
|
|
872
851
|
* 人明确选择入口时在主窗口打开一个会话,和点侧栏里那一行一样;不会自动发送,不替人换屏(只在人点了插件界面上的按钮时调,
|
|
873
852
|
* 如「打开上次会话」打开插件经 `ctx.sessions.launch` 开出来的会话)。没有的会话抛错。
|
|
@@ -900,13 +879,6 @@ export interface UiContext {
|
|
|
900
879
|
toolLine(tool: string, line: ToolLine, options?: {
|
|
901
880
|
match?: "tool" | "prefix";
|
|
902
881
|
}): void;
|
|
903
|
-
/**
|
|
904
|
-
* 给自己的工具画审批卡,见 `ApprovalCardProps`。`tool` 是 `ApprovalRequest.tool`;order 和让位规则同 `toolCard`:
|
|
905
|
-
* 渲染出错时让给下一个,都不行用产品的通用卡。
|
|
906
|
-
*/
|
|
907
|
-
approvalCard(tool: string, component: ComponentType<ApprovalCardProps>, options?: {
|
|
908
|
-
order?: number;
|
|
909
|
-
}): void;
|
|
910
882
|
/**
|
|
911
883
|
* 给 `run.request` 画卡,见 `RequestCardProps`。`kind` 是 `HumanRequest.kind`;order 和让位规则同 `toolCard`:渲染出错时让给下一个,
|
|
912
884
|
* 都不行用产品的通用卡。
|
|
@@ -984,15 +956,18 @@ export interface UiContext {
|
|
|
984
956
|
/** React hook:各插件登记的环境卡片分区,按 order 排好,给画环境卡片的插件用;增减跟着变。只在组件里调。 */
|
|
985
957
|
useEnvironmentSections(): readonly EnvironmentSectionEntry[];
|
|
986
958
|
/**
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
* 只在渲染时同步调,别在里面做耗时的事。一个插件只能有一个,再注册换掉上一个。
|
|
959
|
+
* 输入框 `@` 菜单里加几项,见 `ComposerMention`:每次打开菜单时按输入框的目录和会话问一次,可以是 Promise;
|
|
960
|
+
* 菜单、分组、token 的样子由宿主画。一个插件一份,再登记换掉上一份,停用时撤下。
|
|
990
961
|
*/
|
|
991
|
-
/** 每次打开 `@` 时列出当前目录可用的引用;停用后自动撤销,一个插件一份。 */
|
|
992
962
|
mentions(provider: (context: {
|
|
993
963
|
cwd?: string;
|
|
994
964
|
sessionId?: string;
|
|
995
965
|
}) => ComposerMention[] | Promise<ComposerMention[]>): void;
|
|
966
|
+
/**
|
|
967
|
+
* 输入框空着时的灰字建议(下一步想说什么),按 Tab 整段填进去,人看过再自己发。宿主画输入框时按注册先后问,
|
|
968
|
+
* 取第一个给出字的;只问开着的会话,新对话不问。返回空串或 undefined 表示这次没有建议。
|
|
969
|
+
* 只在渲染时同步调,别在里面做耗时的事。一个插件只能有一个,再注册换掉上一个。
|
|
970
|
+
*/
|
|
996
971
|
composerHint(hint: (session: SessionView) => string | undefined): void;
|
|
997
972
|
/**
|
|
998
973
|
* 换掉欢迎页问候下面的那一行(默认是「要在某个项目里开发什么?」):宿主每次画欢迎页时按界面语言问一次,等答复时那一行先空着,
|
|
@@ -1022,8 +997,13 @@ export interface UiContext {
|
|
|
1022
997
|
/** 关闭自己在该会话打开的面板,仍经过该面板的 onClose;给了 `tab` 只关那一个标签。 */
|
|
1023
998
|
closePanel(id: string, sessionId?: string, tab?: string): void;
|
|
1024
999
|
/**
|
|
1025
|
-
*
|
|
1026
|
-
*
|
|
1000
|
+
* 把一段引用(`QuoteAction.run` 拿到的)放进自己面板 `panelId` 里 `ui.Conversation` 的输入框:会话 `sessionId` 眼前开着的正是这个面板时
|
|
1001
|
+
* 追加到它,不然新开一个标签放进去。草稿原样留着,不发送。面板没登记时抛错。输入框的草稿和引用归宿主,所以由宿主放。
|
|
1002
|
+
*/
|
|
1003
|
+
quoteInPanel(panelId: string, sessionId: string, quote: DraftQuote): void;
|
|
1004
|
+
/**
|
|
1005
|
+
* 在指定会话的文件面板打开已有文件,读取仍受该会话的文件边界约束;会话媒体目录里的图(`ctx.media.save` 交回的 `path`)也算在边界里。
|
|
1006
|
+
* 唯一的例外是这个会话最近一次请求的「记忆文件」(`PromptSection.files` 标的项目指令、项目记忆来源),在边界外也能只读打开。
|
|
1027
1007
|
*/
|
|
1028
1008
|
openFile(path: string, sessionId: string): Promise<void>;
|
|
1029
1009
|
/**
|
|
@@ -1038,8 +1018,9 @@ export interface UiContext {
|
|
|
1038
1018
|
droppedPaths(transfer: DataTransfer): string[] | null;
|
|
1039
1019
|
/** 产品的文件右键菜单;root 用于复制相对路径,hook 只在组件里调用。 */
|
|
1040
1020
|
useFileMenu(root: string): FileMenu;
|
|
1041
|
-
/** 产品的语法高亮 diff
|
|
1021
|
+
/** 产品的语法高亮 diff;插件不需要另带一套代码渲染器。 */
|
|
1042
1022
|
readonly FileDiff: ComponentType<FileDiffProps>;
|
|
1023
|
+
/** 产品的文件预览(代码高亮、图片、文档),内容由插件经自己的 remote 读来,见 `FilePreviewProps`。 */
|
|
1043
1024
|
readonly FilePreview: ComponentType<FilePreviewProps>;
|
|
1044
1025
|
/** 按扩展名注册只读预览;同扩展名后注册的生效,撤下后恢复前一个。 */
|
|
1045
1026
|
documentViewer(extensions: readonly string[], component: ComponentType<DocumentPreviewProps>): void;
|
|
@@ -1071,11 +1052,24 @@ export interface UiContext {
|
|
|
1071
1052
|
* 一个插件只能有一个,再注册换掉上一个。
|
|
1072
1053
|
*/
|
|
1073
1054
|
linkOpener(opener: LinkOpener): void;
|
|
1055
|
+
/**
|
|
1056
|
+
* 本体文件树等处「在终端中打开」由谁接:人点了以后调 `open(directory, sessionId)`,`directory` 是要进去的目录(绝对路径),
|
|
1057
|
+
* `sessionId` 是入口所在的会话。入口是本体的,终端是插件的,所以按种类登记成接手者。每个插件一个、再登记换掉上一个,
|
|
1058
|
+
* 几个插件都登记时最后登记的接,它停用后还给前一个;没人登记时本体提示去装终端插件,`open` 抛错时把错误弹成提示条。
|
|
1059
|
+
*/
|
|
1060
|
+
terminalOpener(open: (directory: string, sessionId: string) => void | Promise<void>): void;
|
|
1061
|
+
/**
|
|
1062
|
+
* 回复里 shell 代码块的「在终端运行」:人点击后调用 `run(command, sessionId)`,`command` 已去掉提示符(`$ `、`> `)和注释行,
|
|
1063
|
+
* 要原样执行;`sessionId` 是代码块所在的会话。登记、让位和没有处理器时的提示同 `terminalOpener`。
|
|
1064
|
+
*/
|
|
1065
|
+
terminalRunner(run: (command: string, sessionId: string) => void | Promise<void>): void;
|
|
1074
1066
|
/**
|
|
1075
1067
|
* 主输入框排队条上,每条排着的消息「更多」菜单里多一项,见 `QueueAction`;排在产品自己的「编辑」后面,几个插件按注册先后。
|
|
1076
1068
|
* 没有插件登记时排队条直接放「编辑」按钮,不出菜单。一个插件只能有一个,再注册换掉上一个。
|
|
1077
1069
|
*/
|
|
1078
1070
|
queueAction(action: QueueAction): void;
|
|
1071
|
+
/** 转录划词后引用菜单里多一项,见 `QuoteAction`。一个插件只能有一个,再注册换掉上一个,停用时撤下。 */
|
|
1072
|
+
quoteAction(action: QuoteAction): void;
|
|
1079
1073
|
/** 用户点击时打开目标项目/目录的空白对话,已有草稿原样保留并追加 text;仍由人按发送。 */
|
|
1080
1074
|
openConversationDraft(options: ConversationDraft): Promise<void>;
|
|
1081
1075
|
/**
|
|
@@ -1165,6 +1159,11 @@ export interface UiContext {
|
|
|
1165
1159
|
* 只在内存里,重开应用后都算看过。只在组件里调,规则同 React 的 hook。
|
|
1166
1160
|
*/
|
|
1167
1161
|
useSessionActivity(sessionId: string): SessionActivity | undefined;
|
|
1162
|
+
/**
|
|
1163
|
+
* 订阅所有会话(包括没打开的)落盘的变化,见 `SessionChange`:只说哪个会话、哪一类变了,给跨会话的页面(画廊)知道该重读了;
|
|
1164
|
+
* 流式生成中的增量不发。返回取消订阅的函数,组件的 effect 收尾时调;插件停用时也撤下。会话事件只到渲染进程的宿主,所以由宿主转。
|
|
1165
|
+
*/
|
|
1166
|
+
onSessionsChanged(listener: (event: SessionChange) => void): () => void;
|
|
1168
1167
|
/**
|
|
1169
1168
|
* 输入框 `/` 菜单此刻有的命令,同名去重后的样子:产品自己的(`/clear`,没有 `plugin`)在前,再是各插件经 `ctx.commands`
|
|
1170
1169
|
* 供的。`cwd` 是按哪个目录列(项目里的命令文件、技能跟着变)。只读,展开、发送仍走输入框。每次调用现问,要看时再调。
|
|
@@ -1184,6 +1183,11 @@ export interface UiContext {
|
|
|
1184
1183
|
readonly ModelSelect: ComponentType<ModelSelectProps>;
|
|
1185
1184
|
/** 输入框里的模型和思考档位那两枚,见 `ModelControlsProps`。 */
|
|
1186
1185
|
readonly ModelControls: ComponentType<ModelControlsProps>;
|
|
1186
|
+
/**
|
|
1187
|
+
* 主输入框的发送键,见 `ComposerSendProps`:发送和停止是同一枚,一轮开始、结束时在原地换。给插件自己画的输入框(画廊的改图条)用。
|
|
1188
|
+
* 跑着时按下停下 `sessionId` 这一轮,和主输入框的停止键一样:只有人按了这枚按钮才停,所以停止由按钮自己做,不开给插件直接调。
|
|
1189
|
+
*/
|
|
1190
|
+
readonly ComposerSend: ComponentType<ComposerSendProps>;
|
|
1187
1191
|
/** React hook:「跟随」会落到的模型和默认思考档位,见 `ModelDefaults`;设置改了跟着变。只在组件里调。 */
|
|
1188
1192
|
useModelDefaults(): ModelDefaults;
|
|
1189
1193
|
/** React hook:登记过的项目,按侧栏的顺序;增删改跟着变。只在组件里调。 */
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -7,8 +7,8 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
7
7
|
|
|
8
8
|
插件是一个目录,最多两个入口,都默认导出 `{ name, apply }`,`name` 相同:
|
|
9
9
|
|
|
10
|
-
- **逻辑入口** `src/index.ts` → `dist/index.mjs`:跑在 agent 进程(Node),`apply(ctx)`
|
|
11
|
-
- **界面入口** `src/ui.tsx` → `dist/ui.mjs`(可选):跑在渲染进程(浏览器,没有 Node),`apply(ui)`
|
|
10
|
+
- **逻辑入口** `src/index.ts` → `dist/index.mjs`:跑在 agent 进程(Node),`apply(ctx)` 里注册工具、往钩子里加层、开接口给界面。细节读 [references/logic.md](references/logic.md)。
|
|
11
|
+
- **界面入口** `src/ui.tsx` → `dist/ui.mjs`(可选):跑在渲染进程(浏览器,没有 Node),`apply(ui)` 里注册卡片、面板、页面、皮肤。细节读 [references/ui.md](references/ui.md),组件查 [references/components.md](references/components.md),样式和文案读 [references/style-i18n.md](references/style-i18n.md)。
|
|
12
12
|
|
|
13
13
|
两个入口一起启用、一起停用。所有注册都记在插件名下,停用或 `apply` 抛错时整体撤销,插件不用自己清理注册。
|
|
14
14
|
|
|
@@ -19,70 +19,79 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
19
19
|
| 能用的 | 用来做什么 |
|
|
20
20
|
| --- | --- |
|
|
21
21
|
| 本 skill | 怎么写、有哪些约束 |
|
|
22
|
-
| 插件 SDK `@plumeai/plugin-sdk` | 类型声明、`plume-plugin` 命令(新建、检查、构建、安装、开发)、tsconfig;下面这些模块名的类型都由它提供。随 Plume
|
|
22
|
+
| 插件 SDK `@plumeai/plugin-sdk` | 类型声明、`plume-plugin` 命令(新建、检查、构建、安装、开发)、tsconfig;下面这些模块名的类型都由它提供。随 Plume 应用发,独立项目经 `plume-plugin init` 链到这一份,不用 npm 装 |
|
|
23
23
|
| `@plume/contract`(逻辑入口)、`@plume/contract/ui`(界面入口) | 接口类型,以类型声明里的注释为准;只能 `import type` |
|
|
24
24
|
| `react`、`react/jsx-runtime`、`@plume/ui`、`@plume/styles` | 界面入口运行时和产品共用同一份实例 |
|
|
25
25
|
| `@plume/ui/icons` | 图标,打包进插件,只打用到的 |
|
|
26
26
|
| `node:*`、自己 `package.json` 里声明的第三方包 | 逻辑入口随便用;界面入口只能用浏览器能跑的 |
|
|
27
27
|
| 仓库里的插件(见文末) | 照着写 |
|
|
28
28
|
|
|
29
|
-
宿主运行时的一切从 `ctx` / `ui`
|
|
29
|
+
宿主运行时的一切从 `ctx` / `ui` 上拿。`@plume/ui` 只开放 components.md 里的名字,引用别的类型检查就失败;`plume-plugin check` 还会查 import、色值和 Tailwind 类、写死的中文(Plume 的两个仓库里是 `pnpm check:plugins`,同一套规则)。
|
|
30
30
|
|
|
31
31
|
## 先判断能不能做成插件
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
33
|
+
逻辑入口能做的(成员和钩子的全部说明在 logic.md):
|
|
34
|
+
|
|
35
|
+
| 要做的事 | 用什么 |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| 给 agent 加工具 | `ctx.tools.register`;给模型的写进 `output`,给卡片的写进 `details`;带图给模型看用 `images` |
|
|
38
|
+
| 工具里跑 shell 命令 | `run.exec`:沙箱档由本体按权限模式定,要更宽一档经 `run.escalate` 申请(只认人答),照 `run.shell()` 的写法拼命令;不自己起进程 |
|
|
39
|
+
| 工具里读、写用户的文件 | `run.files`:本体守读写边界、记改动快照,人撤回时连文件一起退;可能改文件的命令带 `recordChanges` |
|
|
40
|
+
| 工具要问人放不放行,或要人给一份数据(提问、批准计划、填表) | `run.ask`;`run.request`(不是权限,完全访问也照问) |
|
|
41
|
+
| 工具答完后在后台接着做、送回结果,约会话过一会儿接着做,给另一个顶层会话寄信 | `run.background`、`run.track`、`run.schedule`、`run.letter`(除 `track` 外只有顶层会话有) |
|
|
42
|
+
| 在每次工具调用外面加一层:预先放行、拦截、改参数、改结果、整个接管 | 钩子 `tool.execute` |
|
|
43
|
+
| 一次操作要问人时先替人决定:放行、拒绝,或交给里层弹卡 | 钩子 `tool.approve`;完全访问、`approved`、「以后不再问」由本体放行、到不了这一层,提权只认人答 |
|
|
44
|
+
| 每一轮开始、人的消息写进会话前拦下它,或给模型补一段上下文 | 钩子 `prompt.submit` |
|
|
45
|
+
| 每次调模型前按段改系统提示词、裁剪消息、藏起工具(只影响这一次请求) | 钩子 `model.context` |
|
|
46
|
+
| 每一步收尾时让循环多做一步,或给结束换个原因 | 钩子 `step.end` |
|
|
47
|
+
| 换掉压缩的做法 | 钩子 `context.compact` |
|
|
48
|
+
| 给输入框的 `/` 菜单供命令 | `ctx.commands.provide` |
|
|
49
|
+
| 用默认模型做一次性的小调用、读花销、用生图模型出图 | `ctx.model`(`complete`、`usage`、`image`),图存进 `ctx.media` |
|
|
50
|
+
| 读会话、改标题、归档 | `ctx.sessions`(`list`、`messages`、`setTitle`、`setArchived`…) |
|
|
51
|
+
| 自己的子会话(侧边聊天、子 Agent),或页面上一直在的独立会话 | `ctx.sessions.create`,`send` / `start` / `wait` / `report` |
|
|
52
|
+
| 开一个新的顶层会话并发出第一句话;把会话接到 Plume 之外 | `ctx.sessions.launch`;带 `steward: true` 由你代管 |
|
|
53
|
+
| 列项目文件、在隔离视图里读 git、读写本体记下的改动 | `ctx.workspace`、`ctx.changes` |
|
|
54
|
+
| 加一种资源地址(`scheme://路径`) | `ctx.resources` |
|
|
55
|
+
| 跑长期的东西(子进程、定时器、文件)给自己的界面用 | `ctx.serve`(界面经 `ui.remote()` 调),清理登记 `ctx.effect` |
|
|
56
|
+
| 驱动面板里的网页(`<webview>`),或在临时沙箱里检查一页 | `ctx.pages` |
|
|
57
|
+
| HTTP(S) 请求走系统代理 | `ctx.fetch` |
|
|
58
|
+
| 插件之间提供、使用一组方法 | `ctx.provide` / `ctx.use` |
|
|
59
|
+
|
|
60
|
+
界面入口能做的(成员的全部说明在 ui.md):
|
|
61
|
+
|
|
62
|
+
| 要做的事 | 用什么 |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| 工具在对话里的卡片,或只登记它在转录里的说法 | `ui.toolCard`、`ui.toolLine` |
|
|
65
|
+
| 给 `run.request` 画等人卡(审批卡只有产品那张) | `ui.requestCard` |
|
|
66
|
+
| 接管助手正文里某种语言的围栏代码块 | `ui.codeBlock` |
|
|
67
|
+
| 工作区加面板、侧栏加页面、设置页加一节 | `ui.panel`、`ui.page`、`ui.settingsSection` |
|
|
68
|
+
| 输入框周围:底下那排一格、上方项目签那一行一格、上方独占一行、灰字建议 | `ui.composerItem`、`ui.composerChip`、`ui.composerAbove`、`ui.composerHint` |
|
|
69
|
+
| 跟着会话的卡片、最新回复下面的卡片、回复操作排的按钮、环境卡片的一节 | `ui.conversationCard`、`ui.turnFooter`、`ui.replyAction`、`ui.environmentSection` |
|
|
70
|
+
| 画自己的子会话、独立会话 | `ui.Conversation`、`ui.useSession` |
|
|
71
|
+
| 复用产品的差异视图、文件预览、正文渲染、模型下拉 | `ui.FileDiff`、`ui.FilePreview`、`ui.Markdown`、`ui.ModelSelect` |
|
|
72
|
+
| 弹提示条、开关自己的面板、把字和图写进输入框 | `ui.notify`、`ui.openPanel`、`ui.draftInSession` |
|
|
73
|
+
| 换皮肤(只能改 token) | `ui.skin` |
|
|
74
|
+
| 截屏浮层、钉在桌面上的小窗口 | `ui.screen` |
|
|
75
|
+
| 侧栏记号、删除确认里的一句、网址和文件签的打开方式、划词菜单、排队消息的菜单…… | ui.md 的成员表 |
|
|
76
|
+
|
|
77
|
+
以人的名义做的事(以人的身份发送、停下一轮、归档、打开会话)只在人点了按钮时做,不在渲染、定时器里做;停下一轮用 `ui.ComposerSend` 的停止键(交给它 `sessionId`),卡片的 `send` 由宿主核对是真实点击。
|
|
78
|
+
|
|
79
|
+
插件做不到的:
|
|
80
|
+
|
|
81
|
+
- 替换模型调用或 agent 循环;换掉审批卡(本体只有一张);改存下的模型回复(显示时拿掉标记用 `ui.replyText`)。
|
|
82
|
+
- 在没有用户停止操作时让本来要接着做的一轮停下(`step.end` 只能多做或给结束换原因)。
|
|
83
|
+
- 任意往会话里写东西或发起一轮。能写的只有:自己的子会话和独立会话、钩子的 `say` / `context`、后台结果(`report`、`run.background()` 的渠道)、`run.letter` 寄的信(标着来自哪个会话)、`launch` 开出来的新会话的第一句、代管会话里转的话,以及自己的子会话里经 `run.parent` / `handToParent` 排进它父会话的任务。不能往别的已有会话里发、不能指定任意父会话或冒充用户授权。
|
|
84
|
+
- 没人点按钮时替人发送;绕过公开草稿接口操纵输入框;新增钩子或界面扩展点。
|
|
72
85
|
|
|
73
86
|
**需求要用到做不到的能力时,停下来报告缺的是哪个能力、哪个插件需要它**,这是宿主的改动,不在插件里绕过(不改宿主代码、不碰宿主内部对象、不往页面上自己挂 DOM 冒充扩展点)。
|
|
74
87
|
|
|
75
88
|
## 流程
|
|
76
89
|
|
|
77
90
|
1. **定名字**:插件名小写加点分段(Plume 自己提供的插件都以 `plume.` 开头);目录名只用小写字母、数字和连字符,它也是 `plume-plugin://` 的主机名。
|
|
78
|
-
2. **搭目录**:`plume-plugin init <目录>`
|
|
79
|
-
3.
|
|
80
|
-
4.
|
|
81
|
-
5.
|
|
82
|
-
6. **构建和加载**:
|
|
83
|
-
- 独立项目:`plume-plugin dev` 构建、装进 `~/.plume/local-plugins/<目录名>/`(和市场装的同名时这一份生效,不被市场更新换掉,卸掉就回到市场那份),之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install`。
|
|
84
|
-
- Plume 的插件仓库里(`plugins/<目录名>`):`pnpm plugin dev plugins/<目录名>`,和独立项目一样构建、装进本机的 Plume。
|
|
85
|
-
7. **自检**:对照文末清单,再跑检查(独立项目 `plume-plugin check`:类型检查加插件规则;仓库里 `pnpm check`)。
|
|
91
|
+
2. **搭目录**:`plume-plugin init <目录>` 从模板生成,模板里是一个能跑的工具加卡片,改成自己的。命令在 `~/.plume/bin/plume-plugin`(Windows 的 cmd、PowerShell 里是同目录的 `plume-plugin.cmd`;Plume 启动过一次就有,用 Plume 自己的运行时跑,不用装 Node 和 npm)。Plume 的插件仓库(plume-plugins)里用 `pnpm plugin init plugins/<目录名>`。
|
|
92
|
+
3. **写逻辑入口**(logic.md),要画东西时**写界面入口**(ui.md、components.md),有界面或给人看的文字时照 style-i18n.md。
|
|
93
|
+
4. **构建和加载**:独立项目用 `plume-plugin dev`,插件仓库里用 `pnpm plugin dev plugins/<目录名>`:构建、装进 `~/.plume/local-plugins/<目录名>/`(和市场装的同名时这一份生效,不被市场更新换掉,卸掉就回到市场那份),之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install`。
|
|
94
|
+
5. **自检**:对照下面的清单,再跑检查(独立项目 `plume-plugin check`:类型检查加插件规则;仓库里 `pnpm check`)。
|
|
86
95
|
|
|
87
96
|
## 目录骨架
|
|
88
97
|
|
|
@@ -93,7 +102,7 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
93
102
|
├── package.json # plume.displayName、plume.category 是「插件市场」里的名字和分类;仓库里还写 @plumeai/plugin-sdk 等开发依赖
|
|
94
103
|
├── tsconfig.json # 继承 @plumeai/plugin-sdk/tsconfig.json
|
|
95
104
|
├── README.md # 「插件市场」里的介绍:它做了什么、怎么用
|
|
96
|
-
├── images/
|
|
105
|
+
├── images/ # 可选:README 引用的图片(相对路径 `images/xx.png`),随包分发
|
|
97
106
|
├── icon.png # 可选:插件图标,市场、详情和设置页共用,随安装包分发
|
|
98
107
|
├── AGENTS.md、CLAUDE.md、.claude/skills/plume-plugin/ # 只有独立项目有:给接手的 agent
|
|
99
108
|
├── node_modules/ # 只有独立项目有:init 建的链接,指到随 Plume 发的 SDK;坏了跑 plume-plugin link
|
|
@@ -105,15 +114,13 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
105
114
|
└── types.ts # details 的类型和校验,两个入口共用
|
|
106
115
|
```
|
|
107
116
|
|
|
108
|
-
用 serve / remote 时再加 `src/api.ts
|
|
117
|
+
用 serve / remote 时再加 `src/api.ts`,放两个入口共用的接口类型;引入 `.css` 时加 `src/css.d.ts`(写法见 style-i18n.md)。
|
|
109
118
|
|
|
110
|
-
- `package.json` 的 `description`、`keywords`、`plume.displayName`、`plume.category`
|
|
111
|
-
- `plume.defaultPrompt` 可选,几句示例提问(字符串数组,最多显示四句):插件装上并开着时,详情页「试试这样问」列出它们,点一句开新对话、写进输入框。
|
|
119
|
+
- `package.json` 的 `description`、`keywords`、`plume.displayName`、`plume.category` 填好,「插件市场」里显示它们。`plume.defaultPrompt` 可选,几句示例提问(字符串数组,最多显示四句):插件装上并开着时,详情页「试试这样问」列出它们,点一句开新对话、写进输入框。
|
|
112
120
|
- README 里的图片:插件目录里的写相对路径(放在 `images/` 下,支持 PNG、JPEG、GIF、WebP、SVG,每张不超过 5 MiB),网上的写 https 地址;带深色版本的 `<picture>` 按窗口明暗选。
|
|
113
|
-
- 根目录的 `icon.png`
|
|
121
|
+
- 根目录的 `icon.png` 自动作为插件图标:正方形 PNG,建议 192×192,不超过 1 MiB;不提供或图片损坏时显示默认标记。
|
|
114
122
|
- `devDependencies` 只用于类型和打包:逻辑入口用到的第三方包全部打进 `dist/index.mjs`。
|
|
115
123
|
- `dependencies` 只放打不进去的原生模块(如 `node-pty`):构建时连同实际安装的运行依赖、可选依赖和 peer 依赖一起复制进 `dist/node_modules`,只保留 Plume 发包的平台、架构(darwin、win32 的 arm64、x64,加上构建机)的包与预编译文件,随 `dist/` 一起装。复制出的 Mach-O、ELF 文件(如 node-pty 的 `spawn-helper`)由构建补上执行位,不用为此写 postinstall。
|
|
116
|
-
- 引入 `.css` 时加一个 `src/css.d.ts`(写法见 style-i18n.md)。
|
|
117
124
|
|
|
118
125
|
## 自检清单
|
|
119
126
|
|
|
@@ -122,33 +129,33 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
|
|
|
122
129
|
- [ ] 工具结果里给模型的写进 `output`,给卡片的写进 `details`;卡片先校验 `details`,认不出就返回 `fallback`。
|
|
123
130
|
- [ ] 钩子的层:不需要处理的调用原样 `next(input)`;`order` 选得对(见 logic.md);出错直接抛,不吞。
|
|
124
131
|
- [ ] 逻辑入口自己起的定时器、子进程、监听登记了 `ctx.effect`;界面入口在 apply 中启动的订阅、远端 watch 登记 `ui.effect` 清理(如 `ui.effect(() => call.cancel())`),停用、重载或 apply 失败时都会执行。
|
|
125
|
-
- [ ] 界面:没写 Tailwind 类;文字用 `Text`,组件只用
|
|
126
|
-
- [ ]
|
|
132
|
+
- [ ] 界面:没写 Tailwind 类;文字用 `Text`,组件只用 components.md 里列的;自己的 CSS 只管布局,颜色只取 `var(--plume-*)`;浅色、深色都能看。
|
|
133
|
+
- [ ] 给人看的文字中英两份;代码里发给模型的中文标了 `i18n-exempt`。
|
|
127
134
|
- [ ] `README.md` 写了它做什么、怎么用。
|
|
128
135
|
- [ ] 类型检查通过(仓库里 `pnpm check` 通过);改了界面或行为的,装进 Plume 实际跑一遍。
|
|
129
136
|
|
|
130
137
|
## 可以照着写的插件
|
|
131
138
|
|
|
132
|
-
|
|
139
|
+
`todo`、`commands`、`web`、`skills`、`git`、`mcp`、`browser`、`side-chat`、`subagents`、`plan` 随应用发,在本体仓库的 `packages/builtin/` 下;其余在 Plume 插件仓库(plume-plugins)的 `plugins/` 下。拿不到仓库时,以模板和本 skill 里的代码片段为准。
|
|
133
140
|
|
|
134
141
|
| 插件 | 看它的什么 |
|
|
135
142
|
| --- | --- |
|
|
136
|
-
| `todo` |
|
|
137
|
-
| `terminal` | `ctx.serve` / `ui.remote()`
|
|
138
|
-
| `scheduled` | `ctx.dataDir`
|
|
139
|
-
| `git` |
|
|
140
|
-
| `memory` | `model.context`
|
|
141
|
-
| `usage` | 设置 › 数据里的一页(`
|
|
142
|
-
| `screenshot` |
|
|
143
|
-
| `commands` | `ctx.commands.provide
|
|
144
|
-
| `hooks` |
|
|
145
|
-
| `im` | 不给 `page`
|
|
146
|
-
| `index` | `ctx.workspace.files
|
|
147
|
-
| `web` |
|
|
148
|
-
| `mcp` |
|
|
149
|
-
| `skills` | `model.context`
|
|
150
|
-
| `subagents` | `start` / `wait` / `report`
|
|
151
|
-
| `plan` |
|
|
152
|
-
| `browser` |
|
|
153
|
-
| `side-chat` |
|
|
154
|
-
| `image` |
|
|
143
|
+
| `todo` | 工具的 details 让本体的转录认得(`category: "checklist"`,不注册卡片);`step.end` 在本体的结论外面追问一句;工具结果带 `terminate`;`run.calls`;跟着会话的卡片(`ui.conversationCard`)按 `placement` 换外观、`stop` / `carryOn`;插件自带样式表;`messages.ts` |
|
|
144
|
+
| `terminal` | `ctx.serve` / `ui.remote()` 流式输出和取消;原生依赖;`ctx.effect`;多标签面板;点链接打开文件和网址;拖文件进来粘贴路径 |
|
|
145
|
+
| `scheduled` | `ctx.dataDir` 存数据;页面(`ui.page`);常驻定时器按时 `ctx.sessions.launch`;`watch` 推状态、`ui.pageBadge` 挂数目、`ui.composerAbove` 说失败、`ui.openPage`、`ui.openSession` |
|
|
146
|
+
| `git` | 大一点的插件:子进程、`ctx.workspace.git`、`ctx.model.complete`;面板里多个视图、`.cancel()` 叫停远端调用;`ui.composerChip` 的 `send`;`ctx.provide` 提供服务 `session.worktree`;对话自己的工作树(`prompt.submit` 的 `cwd`、`ctx.sessions.onDeleted` / `onArchived`、`ui.sessionMark`、`ui.deleteNote`) |
|
|
147
|
+
| `memory` | `model.context` 按段插提示词、每个会话冻结一份;`ctx.model.complete` 在后台总结;设置分区画进「个性化」页、「智能体」页「会话」卡里一行(`ui.ModelSelect`) |
|
|
148
|
+
| `usage` | 设置 › 数据里的一页(`page: "nav"`、`group: "data"`);`ctx.model.usage`;SVG 自己画的图表 |
|
|
149
|
+
| `screenshot` | `ui.screen`、`ui.screenOverlay`、`ui.pinnedShot`;标注组件;`ui.composerItem`;占导航一行的设置分区 |
|
|
150
|
+
| `commands` | `ctx.commands.provide`(每次现读磁盘);设置分区经 `ui.remote()` 新建、打开、删除文件 |
|
|
151
|
+
| `hooks` | 六个钩子里的四个一起用(`tool.approve` 在 order 2000);`observe: "all"` 和 `ctx.sessions.onEnded`;运行记录 `watch` 推变化、`ui.replyAction`、`ui.composerAbove`、`ui.openSettings`;`ui.useProjects` 切范围 |
|
|
152
|
+
| `im` | 不给 `page` 的设置分区(画进插件详情);密钥经 `ctx.secrets` 存进 `ctx.dataDir`,界面只知道存没存 |
|
|
153
|
+
| `index` | `ctx.workspace.files`;按会话缓存、会话删掉时丢掉;设置分区里 `ui.useProjects` |
|
|
154
|
+
| `web` | 只读的工具(不弹审批卡);API key 不经界面 |
|
|
155
|
+
| `mcp` | 运行中途增减工具;`run.ask`;`run.request` 带 `waiting` 和 `ui.requestCard`;大图放进 `shownImages`;`model.context` 藏工具换成搜索入口;前缀登记卡片;设置分区里的就地编辑、弹窗和 `Popover` 菜单 |
|
|
156
|
+
| `skills` | `model.context` 按磁盘现状加一段;`tool.execute` 按会话状态拒掉调用;`/` 菜单的 `displayText`;`ui.FileDiff` |
|
|
157
|
+
| `subagents` | `start` / `wait` / `report` 后台派发和回报,`steer` 插话;`model.context` 按会话换参数;`step.end` 的 `awaiting`;`ui.Conversation` 局部渲染;「智能体」整页(`page: "agents"`)、`ctx.tools.list()` 挑工具、`ui.ModelSelect` |
|
|
158
|
+
| `plan` | `ui.composerItem` 里的 `ComposerToggle`;`tool.execute` 设 `readOnly`;`run.request` 带 `waiting`、自己画卡;`context.compact` 接上批准的计划;`ui.toolLine`;`ui.environmentSection` |
|
|
159
|
+
| `browser` | `<webview>` + `ctx.pages`;面板 `keepAlive` + `detachable`;`ui.openPanel` 的 `tab` / `background`、`ui.closePanel`;工具结果带图;`ui.linkOpener`;随包的 `skills/` |
|
|
160
|
+
| `side-chat` | 面板里的多段子会话(`ui.Conversation` 带输入框);`prompt.submit` 补上下文;`ctx.changes`;`/btw` 自己办掉(`handled`);`ui.queueAction`;关标签前用 `Confirm` 问一句 |
|
|
161
|
+
| `image` | `ctx.model.image`、`ctx.media`;工具卡片边画边显示;`ui.mediaUrl`、`ui.openImage`、`ui.draftInSession`;画廊页经 `ui.remote()` 扫全部会话、`ui.revealToolCall` |
|