@plumeai/plugin-sdk 0.2.1 → 0.3.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.
Files changed (30) hide show
  1. package/dist/client/ui/src/avatar/AgentAvatar.d.ts +2 -5
  2. package/dist/client/ui/src/i18n/messages/en.d.ts +3 -0
  3. package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +3 -0
  4. package/dist/client/ui/src/image/AnnotateToolbar.d.ts +2 -4
  5. package/dist/client/ui/src/image/Annotator.d.ts +3 -2
  6. package/dist/client/ui/src/image/screenshot-geometry.d.ts +2 -2
  7. package/dist/client/ui/src/inputs/SearchField.d.ts +1 -1
  8. package/dist/client/ui/src/inputs/pickers.d.ts +1 -1
  9. package/dist/client/ui/src/layout/PageTopRow.d.ts +5 -6
  10. package/dist/client/ui/src/motion/RollingText.d.ts +1 -1
  11. package/dist/client/ui/src/motion/loaders.d.ts +4 -4
  12. package/dist/client/ui/src/overlay/Confirm.d.ts +1 -2
  13. package/dist/client/ui/src/overlay/ContextMenu.d.ts +2 -2
  14. package/dist/client/ui/src/overlay/Dialog.d.ts +5 -6
  15. package/dist/client/ui/src/overlay/Menu.d.ts +3 -4
  16. package/dist/client/ui/src/overlay/Popover.d.ts +1 -1
  17. package/dist/client/ui/src/overlay/Tooltip.d.ts +1 -1
  18. package/dist/client/ui/src/primitives/IconButton.d.ts +2 -2
  19. package/dist/client/ui/src/primitives/Text.d.ts +2 -2
  20. package/dist/client/ui/src/scroll/ScrollText.d.ts +1 -1
  21. package/dist/client/ui/src/scroll/Scroller.d.ts +2 -1
  22. package/dist/contract/src/diagnostics.d.ts +39 -1
  23. package/dist/contract/src/service.d.ts +13 -0
  24. package/dist/contract/src/ui.d.ts +72 -68
  25. package/package.json +1 -1
  26. package/skill/SKILL.md +86 -79
  27. package/skill/references/components.md +233 -1418
  28. package/skill/references/logic.md +240 -75
  29. package/skill/references/style-i18n.md +16 -12
  30. package/skill/references/ui.md +170 -123
@@ -22,12 +22,9 @@ export interface AgentAvatarProps {
22
22
  /** 像素边长。 */
23
23
  size?: number;
24
24
  mood?: AvatarMood;
25
- /**
26
- * 悬停时眼睛跟着指针、身子弹一下。默认开。
27
- *
28
- * `host` 是往上找的那个容器:一整行的任何地方悬停都算,而不是非得对准这 20 像素。
29
- */
25
+ /** 悬停时眼睛跟着指针、身子弹一下。默认开。 */
30
26
  interactive?: boolean;
27
+ /** 往上找的那个容器的选择器(`closest`):在它里面任何地方悬停都算,而不是非得对准这 20 像素。 */
31
28
  host?: string;
32
29
  /** 名字,决定眨眼的节奏——七张脸不该踩着同一个拍子。 */
33
30
  seed?: string;
@@ -1107,6 +1107,9 @@ export declare const en: {
1107
1107
  "retry.fixedDetail": string;
1108
1108
  "plugins.failed": string;
1109
1109
  "plugins.waiting": string;
1110
+ "plugins.provides": string;
1111
+ "plugins.serviceReplaces": string;
1112
+ "plugins.serviceReplacedBy": string;
1110
1113
  "plugins.observesAll": string;
1111
1114
  "about.intro": string;
1112
1115
  "about.checking": string;
@@ -969,6 +969,9 @@ export declare const zhCN: {
969
969
  readonly "retry.fixedDetail": "每次大致等这么久,上下浮动两成,免得断线后大家同时重连;服务端明确要求等更久时按服务端(最多 5 分钟)。";
970
970
  readonly "plugins.failed": "加载失败";
971
971
  readonly "plugins.waiting": "等待服务:{services}";
972
+ readonly "plugins.provides": "提供服务:{services}";
973
+ readonly "plugins.serviceReplaces": "{service}(替换了 {plugins} 提供的)";
974
+ readonly "plugins.serviceReplacedBy": "{service}(没生效,生效的是 {plugin} 提供的)";
972
975
  readonly "plugins.observesAll": "能看到所有会话的运行情况:发给模型的内容、模型的回复、等人答的卡";
973
976
  readonly "about.intro": "查看当前版本、更新内容,手动检查更新、设置自动检查的频率。";
974
977
  readonly "about.checking": "正在检查…";
@@ -22,13 +22,11 @@ export declare function AnnotateToolbar({ annotator, onCancel, onSave, onPin, on
22
22
  /** 把这张图写进下载目录。理由同 `onPin`。 */
23
23
  onDownload?: () => void;
24
24
  /**
25
- * 这一条要挪窝了:从哪儿按下的,按下时它在哪儿。
25
+ * 这一条要挪窝了:从哪儿按下的,按下时它在哪儿。挪到哪儿由调用方定;不传就是不能挪。
26
26
  *
27
- * 挪到哪儿是调用方的事——它飘在一张截好的屏幕上,得留在屏幕里——所以这里只说「开始了」,外加
27
+ * 它飘在一张截好的屏幕上,得留在屏幕里,所以这里只说「开始了」,外加
28
28
  * 两个位置。带上 `origin` 而不是让调用方自己去 DOM 里量:这一条的位置只有它自己最清楚,而且
29
29
  * 按住按钮触发的那一次是从定时器里发出的,那会儿 React 的合成事件早就没有 `currentTarget` 了。
30
- *
31
- * 不传就是不能挪。
32
30
  */
33
31
  onGrab?: (grab: {
34
32
  from: {
@@ -61,9 +61,10 @@ export interface Annotator {
61
61
  */
62
62
  render: () => string | null;
63
63
  /**
64
- * 把正在写的那段文字提交掉,如果有的话。返回有没有。
64
+ * 把正在写的那段文字提交掉,如果有的话。返回有没有。任何要把这幅画变成一张图的操作(`render`)都得先调它;
65
+ * 返回 `true` 时画布还没重画,调用方等一帧再读。
65
66
  *
66
- * 任何要把这幅画变成一张图的操作都得先调它。文字在提交之前住在一个 `<textarea>` 里,提交它的是这个
67
+ * 文字在提交之前住在一个 `<textarea>` 里,提交它的是这个
67
68
  * 输入框失去焦点——可工具条故意不拿焦点(`onMouseDown` → `preventDefault`),因为写字时按「粗」必须
68
69
  * 是改这段字的大小,而不是结束它。于是光标还在文字里就按「完成」,什么也没提交:字在屏幕上,在画布
69
70
  * 上面的一个输入框里,而被裁出来的是画布。出来的图上没有那段字。
@@ -26,9 +26,9 @@ export declare const HANDLE_CURSOR: Record<Handle, string>;
26
26
  /** 手柄在哪,表示为选区边上的一个点。 */
27
27
  export declare function handlePoint(rect: Rect, handle: Handle): Point;
28
28
  /**
29
- * 指针在哪个手柄上,没有就是 null。
29
+ * 指针在哪个手柄上,没有就是 null。`tolerance` 是屏幕像素的半径,而不是手柄画出来的大小。
30
30
  *
31
- * `tolerance` 是屏幕像素的半径,而不是手柄画出来的大小:目标得比记号大,理由和滚动条一样——八个像素见方
31
+ * 目标得比记号大,理由和滚动条一样——八个像素见方
32
32
  * 的一块,手落不准。
33
33
  */
34
34
  export declare function hitHandle(rect: Rect, at: Point, tolerance: number): Handle | null;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 搜索框。
2
+ * 搜索框。独占一行的给 `size="comfortable"`,塞进面板标题行的用默认的 `compact`。
3
3
  *
4
4
  * 侧栏的会话搜索、审阅面板的文件筛选、文件树的筛选共用这一个。各写各的话,每个都有自己的高度、
5
5
  * 自己的描边、自己对放大镜在框里还是框旁的看法——一个只有一个答案的问题,得到三个答案。
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { type JSX } from "react";
8
8
  /**
9
- * 带自己画的步进按钮的数字框。
9
+ * 带自己画的步进按钮的数字框。`onChange` 只收到 `min`–`max` 以内的数,敲到一半的草稿留在框里。
10
10
  *
11
11
  * 用 `type="text"` 加数字键盘提示,不用 `type="number"`:后者的上下箭头每个浏览器画得都不一样,而且它认为不合法的
12
12
  * 输入一律报成空字符串,`1e5`、`--3` 看着像收下了,到手却是「什么也没敲」。
@@ -1,15 +1,14 @@
1
1
  import type { HTMLAttributes, ReactNode, Ref } from "react";
2
2
  /**
3
3
  * 自己画顶行的页面(`ui.page` 给了 `topRow: true`)每一栏最上面那一行:坐在窗口顶行的高度上,和侧栏顶上那一行齐平。
4
+ * 整行拖得动窗口、双击能缩放,行里能点的控件(按钮、链接、输入框)自动不拖动,不用一个个标。贴着页面左边的那一栏给
5
+ * `start`(两栏页面的右栏在窄窗口里换成整页时也给),给侧栏收起时浮在这里的红绿灯和前进、后退让出宽度。
6
+ * 高度、内边距之外的排法(间距、两端对齐)由 `className` 补;行里不折行,放不下的东西自己截断。
4
7
  *
5
- * 整行拖得动窗口、双击能缩放;行里能点的控件(按钮、链接、输入框)自动挖成不拖动的,不用一个个标。不给整行
6
- * no-drag:那样这一栏宽的一段就既拖不动窗口、也双击不了。Windows、Linux 上窗口顶上另有一条拖窗口的带子,宿主在根元素上
8
+ * 不给整行 no-drag:那样这一栏宽的一段就既拖不动窗口、也双击不了。Windows、Linux 上窗口顶上另有一条拖窗口的带子,宿主在根元素上
7
9
  * 标 `data-plume-window-header`,这一行跟着改成不拖动(`app-region` 不认 `var()`,只能按选择器切):在卡片里拖窗口会带上系统标题栏的行为(右键系统菜单、拖到屏幕边贴边)。
8
10
  *
9
- * `start` 是贴着页面左边的那一栏:侧栏收起时红绿灯和前进、后退浮在这里,左边让出它们的宽度(宿主经
10
- * `--plume-top-row-inset` 给,侧栏开着时是 0)。两栏页面的右栏在窄窗口里换成整页时,也给 `start`。
11
- *
12
- * 高度、内边距之外的排法(间距、两端对齐)由 `className` 补;行里不折行,放不下的东西自己截断。
11
+ * `start` 让出的宽度由宿主经 `--plume-top-row-inset` 给,侧栏开着时是 0。
13
12
  */
14
13
  export declare function PageTopRow({ start, className, children, ...rest }: {
15
14
  start?: boolean;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export declare const ROLL_VALUE = "plume-roll-value";
8
8
  /**
9
- * 一个值替换另一个值,不管发生在哪里。
9
+ * 一个值替换另一个值,不管发生在哪里。给会换成另一个值的标签用,不给连续跳的读数用。
10
10
  *
11
11
  * 原地变化的字——输入框里的推理强度、刚选的模型、权限模式、「展开显示」变成「收起」——不滚的话,眼睛除了
12
12
  * 字形不同以外收不到任何「发生了什么」的信号:在正拖着的控件上读起来像渲染瑕疵,在刚点过的控件上读起来像
@@ -22,7 +22,7 @@
22
22
  * 几何和那些数字怎么定的,写在 `@plume/styles` 的 `ui/motion.css` 的加载记号一节。
23
23
  */
24
24
  /**
25
- * 一圈虚线,匀速转。
25
+ * 一圈虚线,匀速转:这一条正在跑,顶替的是状态图标(任务清单的一步、工具卡的运行中)。颜色跟着周围的文字。
26
26
  *
27
27
  * 六段等分,所以转过 60° 就回到自身:没有可追踪的端点,眼睛抓不到锚点。这是它敢转、而一段
28
28
  * 弧不敢的原因——一段弧有头,十几个同时在屏幕上,每一个都在自己的相位上把视线勾一下。
@@ -35,7 +35,7 @@ export declare function StatusSpinner({ size, className }: {
35
35
  className?: string;
36
36
  }): import("react").JSX.Element;
37
37
  /**
38
- * 一段亮弧沿着一圈淡轨道跑。
38
+ * 一段亮弧沿着一圈淡轨道跑:你按的那下正在回来,顶替的是刚按下的动作图标或角标。
39
39
  *
40
40
  * 轨道是这个记号的重点,不是装饰:亮弧单独转的话,图形的轮廓跟着弧一起走,在按钮那个方寸
41
41
  * 之间会读成「有东西在里面甩」。垫一圈淡的,形状就恒定是一个完整的圆,动的只有亮处。
@@ -45,9 +45,9 @@ export declare function ActionSpinner({ size, className }: {
45
45
  className?: string;
46
46
  }): import("react").JSX.Element;
47
47
  /**
48
- * 一圈波纹离开静止的中心,颜色一路走过调色板。
48
+ * 一圈波纹离开静止的中心,颜色一路走过调色板:这个会话在跑,给侧栏的会话行和替一个会话报状态的地方。
49
49
  *
50
- * 给侧栏的会话行(替一个会话报状态的地方也用它,同一个会话在两处是同一个记号)。那一列可能同时有好几行在跑,而你还要用它读标题——所以这里连「亮处在走」都不
50
+ * 同一个会话在两处是同一个记号。那一列可能同时有好几行在跑,而你还要用它读标题——所以这里连「亮处在走」都不
51
51
  * 要:什么都不旋转、什么都不位移,余光里它只是缓缓涨落的一团。换成上面那两个的话,一列里并排
52
52
  * 三四个各自明灭,读标题时总有东西在旁边闪。
53
53
  *
@@ -27,9 +27,8 @@ export interface ConfirmOptions {
27
27
  /** 取消按钮上的字,默认「取消」。 */
28
28
  cancelLabel?: string;
29
29
  /**
30
- * `danger` 是默认,因为这个东西本来是给删除用的。
30
+ * `danger` 是默认,给删除这类拿不回来的事;`normal` 给「以后要不要自动做这件事」这类问题。
31
31
  *
32
- * `normal` 留给另一类问题:不是「这个删了拿不回来」,而是「以后要不要自动做这件事」。
33
32
  * 两种都该停下一切来问,但只有前一种该是红的——把每个问题都画成危险,等于没画过危险。
34
33
  */
35
34
  tone?: "danger" | "normal";
@@ -24,9 +24,9 @@ export declare function useContextMenu<T = void>(): {
24
24
  close: () => void;
25
25
  };
26
26
  /**
27
- * 面本身。
27
+ * 面本身。点了任何一行都会关掉它。
28
28
  *
29
- * 每一行都会关掉它,在这里处理而不是每一项各自记得:右键菜单里没有不是动作的行,点完还开着的那个会是
29
+ * 在这里处理而不是每一项各自记得:右键菜单里没有不是动作的行,点完还开着的那个会是
30
30
  * 应用里唯一这样的菜单。监听在冒泡阶段,所以行自己的处理已经先运行了。
31
31
  */
32
32
  export declare function ContextMenu({ anchor, onClose, width, children, }: {
@@ -7,9 +7,9 @@
7
7
  import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
8
8
  import { type Dismiss } from "./Overlay.tsx";
9
9
  /**
10
- * 对话框底下的那一颗按钮。上面写着它会做的那件事。
10
+ * 对话框底下的那一颗按钮。上面写着它会做的那件事:写字,不画图标。
11
11
  *
12
- * **写字,不画图标。** 这些按钮曾经有好几处只放一个 ✕ 和一个 ✓,名字挂在 tooltip 上。工具栏
12
+ * 这些按钮曾经有好几处只放一个 ✕ 和一个 ✓,名字挂在 tooltip 上。工具栏
13
13
  * 里的图标按钮可以这么省——周围一排东西替它说明它属于哪一类;对话框底下这两颗没有这个周围,
14
14
  * 它们是这次操作的结论本身,一个要说清按下去会发生什么,另一个要说清不按会怎样。一个勾在
15
15
  * 「删除会话」「卸载插件」「导入 33 个模型」上长得一模一样,而读出区别的唯一办法是把鼠标停
@@ -44,11 +44,10 @@ export declare function DialogFrame({ title, icon, detail, status, children, act
44
44
  className?: string;
45
45
  } & Omit<HTMLAttributes<HTMLDivElement>, "title" | "children">): import("react").JSX.Element;
46
46
  /**
47
- * 里面放着 `DialogFrame` 的 `Overlay`。
47
+ * 里面放着 `DialogFrame` 的 `Overlay`。`actions` 和 `children` 可以是 `dismiss` 的函数,关对话框的按钮
48
+ * 应该用它:`dismiss()` 播完退场再运行 `onClose`,`dismiss(after)` 改为运行 `after`。
48
49
  *
49
- * `actions` 和 `children` 可以是 `dismiss` 的函数,关对话框的按钮应该用它:`dismiss()` 播完退场再运行
50
- * `onClose`,`dismiss(after)` 改为运行 `after`。按钮直接调用 `onClose` 会在那一帧卸掉对话框——那样
51
- * 只有 Escape 和点遮罩是怎么来的就怎么走。
50
+ * 按钮直接调用 `onClose` 会在那一帧卸掉对话框——那样只有 Escape 和点遮罩是怎么来的就怎么走。
52
51
  */
53
52
  export declare function Dialog({ onClose, width, label, returnFocus, actions, children, ...frame }: {
54
53
  onClose: () => void;
@@ -73,12 +73,11 @@ export declare function MenuLabel({ children }: {
73
73
  children: React.ReactNode;
74
74
  }): import("react").JSX.Element;
75
75
  /**
76
- * 列出超过一屏的菜单顶上的筛选框。
76
+ * 列出超过一屏的菜单顶上的筛选框,放进 `Popover` 的 `header` 槽位,列表滚动时它不动。Escape 先清空再关闭。
77
77
  *
78
78
  * 每个菜单各写一份的话,同样的高度、同样的放大镜、同样的占位文案,每多一份就多一次走样的机会。
79
- * 给 `Popover` 的 `header` 槽位用,列表滚动时它不动;它下面那条线属于那个槽位,不在这里画。
80
- *
81
- * Escape 先清空再关闭,和 `SearchField` 一样:第一次按撤销输入,只有没东西可撤销时按才关掉菜单。
79
+ * 它下面那条线属于 `header` 槽位,不在这里画。Escape 和 `SearchField` 一样:第一次按撤销输入,
80
+ * 只有没东西可撤销时按才关掉菜单。
82
81
  */
83
82
  export declare function MenuSearch({ value, onChange, placeholder, autoFocus, onKeyDown, }: {
84
83
  value: string;
@@ -93,7 +93,7 @@ export interface PopoverProps {
93
93
  /** 加在滚动正文上,给自己给内容加内衬的面。 */
94
94
  bodyClassName?: string;
95
95
  /**
96
- * 一列行,还是一样东西。
96
+ * 一列行(`menu`),还是一样东西(`panel`:整个正文交给内容,滑块浮在上面)。
97
97
  *
98
98
  * `menu` 是每个菜单要的内衬:圆角需要地方,行不能跑到滚动条底下,渐隐的边说明列表在那之外还有。
99
99
  * 这三样对只装一块自己画底色的面板的面都是错的——比如一段 diff,它的代码底色会在离卡片右边 25px、
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 应用的悬停提示,写成 JSX 包装,比属性读起来顺。
2
+ * 应用的悬停提示,代替浏览器的 `title`。写成 JSX 包装,比直接写 `data-plume-tip` 属性读起来顺,两种写法表现一样。
3
3
  *
4
4
  * 它只给子元素打标记——气泡本身由 `tooltip-attribute.ts` 里那一个文档级监听器画。所以两种写法表现完全一样,
5
5
  * 这很重要,因为第三方库自己建的按钮只能靠属性够到。
@@ -1,12 +1,12 @@
1
1
  import type { DataAttributes } from "./Button.tsx";
2
2
  /**
3
- * 标签是一个图标的按钮。
3
+ * 标签是一个图标的按钮。`label` 同时是悬停提示和无障碍名。
4
4
  *
5
5
  * 各个工具栏各写各的,会长出五种高度、三种圆角,按下的反馈从没有到缩小 10% 都有。缩小是有意去掉的——
6
6
  * 一个在接触那一刻从指针底下跳开的控件读起来不稳,在这么小的尺寸上那是一下晃动而不是按下。按下用底色
7
7
  * 深一档表示,真的按钮就是这样:它待在原地,只是换了状态。
8
8
  *
9
- * 标签同时是悬停提示和无障碍名,调用方没法只给一个——只有图标的工具栏就是这样变得读屏读不出来的。
9
+ * 标签的两个用处调用方没法只给一个——只有图标的工具栏就是这样变得读屏读不出来的。
10
10
  * 类名组合在 `@plume/styles` 的 `iconButton`。
11
11
  */
12
12
  export declare function IconButton({ label, ariaLabel, icon, onClick, active, disabled, emphasis, badge, explainDisabled, tone, size, tipSide, menu, expanded, className, ...data }: DataAttributes & {
@@ -50,9 +50,9 @@ export declare function Text({ size, tone, weight, as, mono, numeric, className,
50
50
  /** 等宽字体,给正文里引用的标识符和路径。 */
51
51
  mono?: boolean;
52
52
  /**
53
- * 等宽数字。
53
+ * 等宽数字,给原地变化的数——token 数、计时、diff 统计。
54
54
  *
55
- * 给原地变化的数——token 数、计时、diff 统计。比例数字每跳一下字就左右挪,读起来是闪烁。
55
+ * 比例数字每跳一下字就左右挪,读起来是闪烁。
56
56
  */
57
57
  numeric?: boolean;
58
58
  className?: string;
@@ -1,6 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  /**
3
- * 一行字,到边上渐隐而不是以省略号结尾,所在的行被悬停时自己读出来。
3
+ * 一行字,到边上渐隐而不是以省略号结尾,所在的行被悬停时自己读出来:那一行(父级)要带 `plume-scroll` 类。
4
4
  *
5
5
  * 省略号只报告标题被截了,不说截掉的是什么——每个以「帮我梳理这个项目的整体架构」开头的会话都缩成
6
6
  * 同一行没法用的字。渐隐说的是同一件事,但更安静,悬停滚动则真正回答了那个问题。
@@ -19,7 +19,8 @@ export declare function Scroller({ children, className, contentClassName, top, f
19
19
  className?: string;
20
20
  contentClassName?: string;
21
21
  /**
22
- * 滚到自己的边界之后,滚轮还传不传给外面。
22
+ * 滚到自己的边界之后,滚轮还传不传给外面。页面里的一段内容(嵌在外层滚动面里的工具卡片、代码输出、
23
+ * 一列搜索结果)给 `"auto"`;独立的面(弹窗、浮层、菜单)用默认的 `"contain"`。
23
24
  *
24
25
  * 判断标准是这一块**是页面里的一段内容,还是一块独立的面**。一段内容——设置页里的发版说明、一列
25
26
  * 搜索结果——读到底了还想接着往下看页面,是很自然的一件事,那就该给 `"auto"`。一块
@@ -1,5 +1,33 @@
1
1
  import type { AssistantMessage, Message, ToolSpec } from "../../core/llm/types.d.ts";
2
+ import type { HookName } from "./plugin.ts";
2
3
  import type { AgentEvent, TurnStop } from "./service.ts";
4
+ /**
5
+ * 钩子的一层在一次钩子调用里做了什么。只记有没有改,不记改成了什么:观察里不因此多出参数、结果的内容。
6
+ * 「改了」按值比较(同一个对象、或逐个字段一样都算没改),所以照原样展开再交下去不算改。
7
+ */
8
+ export interface HookPass {
9
+ plugin: string;
10
+ order: number;
11
+ /**
12
+ * `next`:往里交了;`answered`:没往里交,自己给了结果;`failed`:这一层抛了错。里层的错原样往外传时只记在出错的那一层,
13
+ * 外面的层照样是 `next`(和 `HookError` 的归因一样)。
14
+ */
15
+ outcome: "next" | "answered" | "failed";
16
+ /** 交给里层的和自己收到的不一样:改了参数,或在输入上设了别的(`approved`、`readOnly`……)。 */
17
+ changedInput?: true;
18
+ /** 交回外层的和里层交给它的不一样:改了结果。 */
19
+ changedOutput?: true;
20
+ }
21
+ /** 一次钩子调用穿过的层,外层在前;一层交了几次(重试)就记几次。 */
22
+ export interface HookTrace {
23
+ hook: HookName;
24
+ layers: HookPass[];
25
+ /**
26
+ * 最里层(本体):`ran` 走到了;`stopped` 本体按外层设下的东西在某一层之前就停下、不再往里交(如外层设了 `readOnly`,写、改文件的工具不跑)。
27
+ * 没有时是某一层自己交了结果或出错,没走到最里层。
28
+ */
29
+ inner?: "ran" | "stopped";
30
+ }
3
31
  /** 会话的只读运行观察,不包含供应商配置、请求头和密钥。观察者失败不影响运行。 */
4
32
  export interface SessionObservation {
5
33
  sessionId: string;
@@ -20,7 +48,17 @@ export interface SessionObservation {
20
48
  type: "response";
21
49
  id: string;
22
50
  reply: AssistantMessage;
23
- } | {
51
+ }
52
+ /**
53
+ * 一次钩子调用经过了哪几层、每层做了什么,有插件挂了这个钩子时才有。`tool.execute`、`tool.approve` 带它所在的那次工具调用
54
+ * (`tool.approve` 一次调用里问几次就有几条),`model.context` 带组出来的那次请求(同 `request` 的 `id`;组了没发出去的不记),
55
+ * `prompt.submit` 两样都不带(每条要写进会话的消息一条,拦下的也记)。`step.end`、`context.compact` 不记。
56
+ */
57
+ | ({
58
+ type: "hooks";
59
+ toolCallId?: string;
60
+ requestId?: string;
61
+ } & HookTrace) | {
24
62
  type: "running";
25
63
  running: boolean;
26
64
  }
@@ -515,6 +515,19 @@ export interface PluginStatus {
515
515
  waitingFor?: string[];
516
516
  /** 插件声明了要看全部会话的运行观察(`Plugin.observe`)。 */
517
517
  observe?: "all";
518
+ /** 这个插件经 `ctx.provide` 提供的服务,按服务名排;没提供时没有。 */
519
+ provides?: ProvidedService[];
520
+ }
521
+ /**
522
+ * 插件提供的一项服务。同名服务只有一个生效的提供方(非内置的优先,同类里先加载的优先),别的插件 `ctx.use` 拿到的是它那一份。
523
+ * 同名的还有别的插件在提供时,生效的那一份写 `replaces`、没生效的写 `replacedBy`。
524
+ */
525
+ export interface ProvidedService {
526
+ name: string;
527
+ /** 这一份生效,同名服务另几个提供方的插件名被它顶下(装一个提供同名服务的插件替换内置的,就是这样)。 */
528
+ replaces?: string[];
529
+ /** 这一份没生效:生效的是这个插件的。 */
530
+ replacedBy?: string;
518
531
  }
519
532
  export type AgentEvent = {
520
533
  type: "fileRemoved";