@plumeai/plugin-sdk 0.1.7 → 0.2.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.
@@ -1,11 +1,11 @@
1
1
  /**
2
- * 资源地址:`scheme://路径`,模型在 read(和 ls)里像文件路径一样用它读技能正文、会话转录、插件说明、被折叠的大输出。
2
+ * 资源地址:`scheme://路径`,模型在读文件的工具里像文件路径一样用它读技能正文、会话转录、插件说明、被折叠的大输出。
3
3
  *
4
4
  * 一个命名空间一个工具的话,每多一类可读的东西就多一个工具、一份参数、一段提示词;这些其实都是「从某个命名空间读一样东西」,
5
- * 所以模型只学一次 `read`,能读的东西随地址变多,工具表不变。
5
+ * 所以模型只学一次读文件,能读的东西随地址变多,工具表不变。
6
6
  *
7
- * 由内置插件 plume.tools 提供服务 `resources`:它自己有 `scratch://`(会话的临时目录,唯一可写的)、`artifact://`、
8
- * `session://`、`plugin://`,别的插件经 `register` 加自己的 scheme(技能插件的 `skill://`)。
7
+ * 本体提供(`PluginContext.resources`),插件换不掉:本体自己有 `scratch://`(会话的临时目录,唯一可写的)、`artifact://`、
8
+ * `session://`、`plugin://`,插件经 `register` 加自己的 scheme(技能插件的 `skill://`)。解析统一走 `ToolRun.files.read`。
9
9
  */
10
10
  /** 拆开的地址。 */
11
11
  export interface ResourceAddress {
@@ -15,7 +15,7 @@ export interface ResourceAddress {
15
15
  path: string;
16
16
  /** `path` 按 `/` 拆开,去掉空段。 */
17
17
  segments: string[];
18
- /** 末尾的 `:10-40` 或 `:7`,要的是这几行;切行由服务做,scheme 不用管。 */
18
+ /** 末尾的 `:10-40` 或 `:7`,要的是这几行;切行由本体做,scheme 不用管。 */
19
19
  range?: {
20
20
  from: number;
21
21
  to?: number;
@@ -33,8 +33,8 @@ export interface Resource {
33
33
  */
34
34
  label?: string;
35
35
  /**
36
- * 内容不是这个项目、这个应用写的(第三方插件的说明、另一次会话的转录、命令的原始输出)时写明来路:read 把它包进
37
- * `<resource origin="…">`,系统提示词说那里面的是数据、不是指令。
36
+ * 内容不是这个项目、这个应用写的(第三方插件的说明、另一次会话的转录、命令的原始输出)时写明来路:读它的工具把它包起来
37
+ * (plume.tools 的 read 包进 `<resource origin="…">`),系统提示词说那里面的是数据、不是指令。
38
38
  */
39
39
  origin?: string;
40
40
  /** 放进工具结果 `details` 的附加信息。 */
@@ -53,25 +53,38 @@ export interface ResourceScope {
53
53
  scratchDir: string;
54
54
  signal?: AbortSignal;
55
55
  }
56
- /** 一个 scheme。只读:唯一可写的是服务自己的 `scratch://`,模型写不了它该遵守的技能正文。 */
56
+ /** 一个 scheme。只读:唯一可写的是本体自己的 `scratch://`,模型写不了它该遵守的技能正文。 */
57
57
  export interface ResourceHandler {
58
58
  /** 小写字母开头,字母、数字、连字符。 */
59
59
  scheme: string;
60
- /** 系统提示词「Addresses」里这个 scheme 的那一行:有哪几种写法、各是什么。 */
60
+ /** 系统提示词的地址清单里这个 scheme 的那一行:有哪几种写法、各是什么。 */
61
61
  describe: string;
62
62
  /** 读一个地址。没有这样东西、越界时抛错,错误信息直接交给模型。路径为空(`scheme://`)的不走这里,走 `list`。 */
63
63
  resolve(address: ResourceAddress, scope: ResourceScope): Promise<Resource>;
64
64
  /** `read <scheme>://` 列出的东西;不给就是不能列。 */
65
65
  list?(scope: ResourceScope): Promise<ResourceEntry[]>;
66
- /** 这个地址在磁盘上指的目录或文件(ls 用它列目录);不给、或返回 null 时 ls 不认这个地址。越界时抛错。 */
66
+ /** 这个地址在磁盘上指的目录或文件(列目录时用,见 `RunFiles.read` 的 `directory`);不给、或返回 null 时列目录不认这个地址。越界时抛错。 */
67
67
  locate?(address: ResourceAddress, scope: ResourceScope): Promise<string | null>;
68
68
  }
69
- /** 服务 `resources`:资源地址,见本文件开头。 */
70
- export interface ResourcesService {
71
- /** 加一个 scheme,返回撤下它的函数(停用时用 `ctx.effect` 撤)。scheme 已被占用、写法不对时抛错。 */
69
+ /** 一个登记着的 scheme,见 `PluginResources.list`。 */
70
+ export interface ResourceScheme {
71
+ scheme: string;
72
+ /** 同 `ResourceHandler.describe`。 */
73
+ describe: string;
74
+ /** 能写:只有本体的 `scratch://`。 */
75
+ writable: boolean;
76
+ }
77
+ /** `ctx.resources`:资源地址,见本文件开头。 */
78
+ export interface PluginResources {
79
+ /**
80
+ * 加一个 scheme,返回撤下它的函数;插件停用时本体替它撤下。登记的只留读的几样(`ResourceHandler` 上的方法),塞了 `write` 也写不进去。
81
+ * scheme 已被占用、写法不对时抛错。
82
+ */
72
83
  register(handler: ResourceHandler): () => void;
84
+ /** 现在登记着的全部 scheme,按名字排:要在提示词里告诉模型有哪些地址、要认出一个参数是不是地址时用。 */
85
+ list(): ResourceScheme[];
73
86
  /**
74
- * 把一大块原文(被剪掉的工具结果)存起来,换回 `artifact://<id>` 地址,模型要时用 read 按窗口取回。
87
+ * 把一大块原文(被剪掉的工具结果)存起来,换回 `artifact://<id>` 地址,模型要时按窗口取回。
75
88
  * 地址按 `tool` 和内容算:同一段原文每次换回同一个地址,从历史重建出的占位标记逐字不变。
76
89
  * 按会话存在内存里,每个会话最多留 30 份、超了丢最旧的;重开应用、会话删掉后就没有了。
77
90
  */
@@ -6,7 +6,7 @@ import type { RetryPolicy } from "../../core/llm/config/retry-policy.d.ts";
6
6
  import type { ApiFormat, AssistantMessage, Message, MessageAttachment, ModelPricing, ThinkingLevel, ThinkingOption, UserContent } from "../../core/llm/types.d.ts";
7
7
  import type { CallContext } from "../../infra/rpc/src/index.d.ts";
8
8
  import type { RevertPlan } from "./changes.ts";
9
- import type { CommandContext, CommandResult, Locale, SlashCommand } from "./plugin.ts";
9
+ import type { CommandContext, CommandResult, Locale, RegisteredToolInfo, SlashCommand } from "./plugin.ts";
10
10
  import type { ClearRange, ClearResult, StorageUse, UsageRequestBucket } from "./usage.ts";
11
11
  /**
12
12
  * 模型设置:若干供应商,每个供应商下若干模型,一个全局默认模型;会话可以另选一个。
@@ -73,6 +73,19 @@ export interface SettingsView {
73
73
  thinking: ThinkingLevel;
74
74
  /** 模型请求的重试策略,已收好范围;改了正在重试的请求从下一次等待起就用新值。 */
75
75
  retryPolicy: RetryPolicy;
76
+ /** 人答过「以后不再问」的批准,按记下的先后;只来自本机的设置,项目配置里写的不算。 */
77
+ alwaysAllow: AlwaysAllow[];
78
+ }
79
+ /**
80
+ * 「以后不再问」的一条批准:`subject`(`ApprovalRequest.subject`)记在谁名下。
81
+ * - 有 `plugin`:发起审批的那件工具的注册插件——插件经 `run.ask` 给的 subject(命令、文件路径、站点),本体替没写 `asks` 的
82
+ * 会改动工具在执行前问的(subject 是工具名)。只放行同一个插件的工具再问同一个 subject,别的插件写出同样的字符串也不算。
83
+ * - 没有 `plugin`:本体自己给的 subject(读边界的 `read:<项目>`),不管哪个工具来读都是同一份批准;插件给的 subject 碰不到它。
84
+ * 插件的名字由本体按注册记,不取插件自己说的,所以插件冒充不了别的插件,也冒充不了本体。
85
+ */
86
+ export interface AlwaysAllow {
87
+ subject: string;
88
+ plugin?: string;
76
89
  }
77
90
  /**
78
91
  * 登记过的项目:一件事和它所在的文件夹。同一个文件夹可以登记成几个项目,所以会话记的是项目 id,不靠目录认项目。
@@ -119,14 +132,23 @@ export interface SessionSummary {
119
132
  updatedAt: number;
120
133
  running: boolean;
121
134
  /**
122
- * 它派出去的后台运行(子智能体之类,见 `SessionAccess.start`)还没都回来,或者交回的结果攒着、还没送进会话。
123
- * 和 `running` 各管各的:`running || background` 才是这件事还没做完(回答底下的交付卡片等它)。只在内存里,不存盘。
135
+ * 它还有后台的事没完:派出去的后台运行(子智能体之类,见 `SessionAccess.start`)还没都回来,交回的结果攒着、还没送进会话,
136
+ * 登记着的一次性后台任务(`ToolRun.track`)没结束。挂着的不算:监视、开发服务器这类长跑的任务,约好的唤醒和 cron,见 `ParentStatus`。
137
+ * 被人停掉、撤回而作废的不算。和 `running` 各管各的:`running || background` 才是这件事还没做完(回答底下的交付卡片等它)。
138
+ * 只在内存里,不存盘。
124
139
  */
125
140
  background?: boolean;
126
141
  /** 这个会话选的模型;没选、或者选的已经不可用时用默认模型。 */
127
142
  model?: ModelRef;
128
143
  /** 这个会话选的思考档位;没选时用模型的默认档,模型没有这一档时就近取一档。 */
129
144
  thinking?: ThinkingLevel;
145
+ /**
146
+ * 工作目录里的项目配置(`.plume/config.json`)给这个会话定的权限模式,而且生效了:项目配置只能收紧权限,比本机设置宽的
147
+ * 本体不认,所以有它时它就是这个会话实际按的那一档(不比本机的 `SettingsView.permissionMode` 宽),没有时按本机设置。
148
+ * 本体每次读这个会话的设置时重算(每一轮、`AgentService.getSession`、本机换了权限模式、设置页读这个目录的项目配置),变了推一次 `sessions`。
149
+ * 只在内存里,不存盘。
150
+ */
151
+ projectPermission?: PermissionMode;
130
152
  /** 上一轮是怎么结束的;正在跑、还没跑过、或者上次没来得及记下(进程崩了)时没有。 */
131
153
  stop?: TurnStop;
132
154
  /** 归档了:移出侧栏,记录和用量都还在,在设置 › 已归档的聊天里取消归档或删除。 */
@@ -145,6 +167,14 @@ export interface SessionSummary {
145
167
  * 和子会话一样不进侧栏。侧栏里的会话两样都没有。
146
168
  */
147
169
  owner?: string;
170
+ /**
171
+ * 这是哪一种会话,由本体按 `parentId`、`owner` 算出,不存盘:
172
+ * - `top`:侧栏里的会话(人新建的、插件经 `ctx.sessions.launch` 开的、分叉出来的),两样都没有;
173
+ * - `child`:插件建的子会话(`SessionAccess.create` 给了 `parentId`),挂在一个 `top` 会话下面;
174
+ * - `standalone`:插件的独立会话(`SessionAccess.create` 不给 `parentId`),有 `owner` 没有 `parentId`。
175
+ * 只有 `top` 进侧栏、搜索、`@` 引用,收后台结果和会话间的信。判断「是不是人的那段对话」看它,不要自己拼 `parentId`、`owner`。
176
+ */
177
+ kind: SessionKind;
148
178
  /** 插件经 `ctx.sessions.launch` 从哪个会话开出来的(计划交出去执行的那个会话、那次调用)。会话本身独立保存,侧栏同组时嵌套在来源下面。 */
149
179
  spawnedFrom?: {
150
180
  sessionId: string;
@@ -154,9 +184,17 @@ export interface SessionSummary {
154
184
  forkedFrom?: {
155
185
  sessionId: string;
156
186
  };
187
+ /**
188
+ * 代管这个会话的插件:它经 `ctx.sessions.launch({ steward: true })` 开出这个会话,之后能往里转话(`SessionAccess.post`)、
189
+ * 替人答它的审批和等人的卡(`SessionAccess.respond`、`answer`)。人在会话菜单里「停止代管」(`AgentService.revokeSteward`)后去掉。
190
+ * 只记谁能这样做,不改会话的种类:代管的会话照旧是人的顶层会话,分叉出来的不带它。
191
+ */
192
+ steward?: string;
157
193
  /** 记录里有几条消息(人说的、回复、工具结果都算),给已归档的页和删除前的问题说「多少」。 */
158
194
  messageCount?: number;
159
195
  }
196
+ /** 会话的种类,见 `SessionSummary.kind`。 */
197
+ export type SessionKind = "top" | "child" | "standalone";
160
198
  /**
161
199
  * 一轮为什么结束:答完了(done)、人按了停(aborted)、出错(error)、用满了步数(max_turns)、
162
200
  * 原地打转被叫停(stalled)、应用退出时还在跑(interrupted)。
@@ -244,39 +282,64 @@ export interface ProviderImportEntry {
244
282
  /** 文件里没带 key,被替换的那个有,导入后留着本机的。 */
245
283
  keepsLocalKey: boolean;
246
284
  }
247
- /** 正在等用户决定的一次工具调用。 */
285
+ /** 正在等用户决定的一次工具调用。和 `HumanRequest` 同排一队,`type` 分开两者。 */
248
286
  export interface ApprovalRequest {
287
+ type?: "approval";
249
288
  toolCallId: string;
250
289
  tool: string;
251
- /** 要做的是哪一类事:本体工具是 `bash`、`write`、`edit`,插件的工具是它的名字。卡片按它写一个小标签。 */
290
+ /**
291
+ * 要做的是哪一类事,卡片按它写一个小标签:是 `ToolKind`(工具做的是哪种活)里的 `command`、`create`、`modify`、`view` 时写「执行命令」「写入文件」
292
+ * 「修改文件」「读取文件」,别的(插件的工具常写自己的名字)照原样写。
293
+ */
252
294
  kind: string;
253
295
  title: string;
254
296
  /** 卡片正文:命令、diff,插件的工具是它的参数。 */
255
297
  detail: string;
256
- /** 「以后不再问」记下的东西:命令或文件的绝对路径。没有时只能答这一次。 */
298
+ /**
299
+ * 「以后不再问」记下的东西:命令、文件的绝对路径、站点。没有时只能答这一次。本体按发起审批的一方分开记、分开比对:工具经 `run.ask` 给的
300
+ * (和本体替没写 `asks` 的工具在执行前问的,subject 是工具名)记在注册这件工具的插件名下,只放行这个插件的工具再问同一个 subject,
301
+ * 别的插件写出同样的字符串不算;本体自己给的(`run.files` 读边界的 `read:<项目>`)记在本体名下,插件给的 subject 碰不到它。
302
+ */
257
303
  subject?: string;
258
304
  /** 为什么要问:插件的层自己问时给的理由,或者模型申请提权时说的那句话。 */
259
305
  reason?: string;
260
306
  /** 申请把这一条命令放到更宽的沙箱模式里跑。只管这一次,答「以后不再问」也不会记下。 */
261
307
  escalation?: "workspace-write" | "danger-full-access";
262
308
  /**
263
- * 问的是人的决定(批准一份计划),不是权限:完全访问、外层放行、「以后不再问」都不替人答,总是弹给人。
264
- * 没有 `subject`,只能答这一次。
309
+ * 这张卡等到什么时候(毫秒时间戳),到时没人答按拒绝收场:等 5 分钟。本体弹卡时填,插件经 `run.ask` 给的不算。
310
+ * 给时刻而不是时长:事件跨进程送到、画出来时,时长已经不准了。
265
311
  */
266
- userOnly?: boolean;
312
+ expiresAt?: number;
313
+ }
314
+ /** 人怎么答:允许这一次、以后都不再问这一项、拒绝。 */
315
+ export type ApprovalDecision = "once" | "always" | "reject";
316
+ /**
317
+ * 一次工具调用停下来等人给的一份数据(`ToolRun.request`):回答问题、批准计划、填表。不是权限:不经钩子 tool.approve,权限模式
318
+ * 和「以后不再问」都管不着它,总是弹给人。和审批同排一队、同占输入框上方那一格(`SessionView.approval`),撤卡规则相同。
319
+ * 人经 `AgentService.answer` 交回一份数据,或经 `respond(…, "reject")` 不答。
320
+ */
321
+ export interface HumanRequest {
322
+ type: "request";
323
+ toolCallId: string;
324
+ tool: string;
325
+ /** 哪一类请求,界面按它找插件登记的卡(`ui.requestCard`)。 */
326
+ kind: string;
327
+ title: string;
328
+ /** 卡片要画的数据(问题、计划、表单),插件给的原样(能结构化克隆的值)。 */
329
+ payload?: unknown;
267
330
  /**
268
331
  * 等这张卡时一句短的状态(按界面语言),画在对话末尾的运行行、侧栏的悬停和系统通知里:「等你批准计划」「等你填写表单」。
269
- * 不给时本体按 `userOnly` 说「等待你的回答」或「等待你的批准」。
332
+ * 不给时本体说「等待你的回答」。
270
333
  */
271
334
  waiting?: string;
272
335
  /**
273
- * 这张卡等到什么时候(毫秒时间戳),到时没人答按拒绝收场:权限请求等 5 分钟,`userOnly` 的(提问、表单、计划)等
274
- * 30 分钟。本体弹卡时填,插件经 `run.ask` 给的不算。给时刻而不是时长:事件跨进程送到、画出来时,时长已经不准了。
336
+ * 这张卡等到什么时候(毫秒时间戳),到时没人答按过期收场:等 30 分钟,比审批久得多——没答多半是人走开了一会儿。
337
+ * 本体弹卡时填,同 `ApprovalRequest.expiresAt`。
275
338
  */
276
339
  expiresAt?: number;
277
340
  }
278
- /** 人怎么答:允许这一次、以后都不再问这一项、拒绝。 */
279
- export type ApprovalDecision = "once" | "always" | "reject";
341
+ /** 输入框上方那一格里的一张卡:审批或等人给数据。 */
342
+ export type PendingRequest = ApprovalRequest | HumanRequest;
280
343
  /**
281
344
  * 用户发出去的一条消息。
282
345
  *
@@ -304,7 +367,7 @@ export interface OutgoingMessage {
304
367
  steer?: boolean;
305
368
  }
306
369
  /**
307
- * 排在一个会话后面、等着轮到自己的一条(输入框上方那几行)。会话忙时人按下回车的,和侧边聊天交过来的任务排的是同一条队。
370
+ * 排在一个会话后面、等着轮到自己的一条(输入框上方那几行)。会话忙时人按下回车的,和子会话交过来的任务排的是同一条队。
308
371
  * 存在 agent 进程里、落盘:关了窗口、重开应用还在。这一轮以完成结束时发下一条;停了、出错了留着等人。
309
372
  */
310
373
  export interface QueuedMessage {
@@ -313,17 +376,23 @@ export interface QueuedMessage {
313
376
  preview: string;
314
377
  /** 轮到它时整份发出去的东西。 */
315
378
  message: OutgoingMessage;
316
- /** 不是人在这个会话的输入框里排的:`side-chat` 是侧边聊天交过来的任务,发出去的消息带着同样的来处(`UserMessage.origin`)。 */
317
- origin?: "side-chat";
318
- /** 侧边聊天交过来的:是哪一个子会话交的,它的回执条(`HandedTask`)据此列出这一条。 */
319
- sideId?: string;
379
+ /**
380
+ * 不是人在这个会话的输入框里排的:`child` 是它的一个子会话交过来的任务,`plugin` 是代管它的插件转进来的话(`SessionAccess.post`)。
381
+ * 发出去的消息带着同样的来处(`UserMessage.origin`)。
382
+ */
383
+ origin?: "child" | "plugin";
384
+ /** 子会话交过来的:是哪一个子会话交的,它的任务列表(`ChildTask`)据此列出这一条。 */
385
+ childId?: string;
386
+ /** 代管插件转进来的:是哪个插件,发出去时记进 `UserMessage.fromPlugin`。 */
387
+ fromPlugin?: string;
320
388
  createdAt: number;
321
389
  }
322
390
  /**
323
- * 侧边聊天交给父会话的一件事走到了哪:子会话的回执条一行一件。还排着的就是父会话队列里那一条(`id` 相同),
391
+ * 子会话交给父会话的一件事(`ParentSession.enqueue`、卡片上的「交给主会话」)走到了哪。只有宿主知道父会话的一轮怎么收场,
392
+ * 所以状态由宿主给;画成什么样由建这个子会话的插件定(`ConversationProps.tasks`)。还排着的就是父会话队列里那一条(`id` 相同),
324
393
  * 开始跑以后的状态宿主只记在内存里,重开应用就没了;做了什么在父会话的转录里,这里只是回执。
325
394
  */
326
- export interface HandedTask {
395
+ export interface ChildTask {
327
396
  id: string;
328
397
  /** 交过去的指令原文。 */
329
398
  text: string;
@@ -336,7 +405,7 @@ export interface HandedTask {
336
405
  error?: string;
337
406
  /** 没出错却没做完:原地打转被叫停、用满了步数。 */
338
407
  unfinished?: "stalled" | "max_turns";
339
- /** 父会话被停下、这件跟着停的是 `stop`(还能继续);人从回执条或计划作废里收回的是 `user`。 */
408
+ /** 父会话被停下、这件跟着停的是 `stop`(还能继续);人收回的(撤回、计划作废)是 `user`。 */
340
409
  cancelledBy?: "user" | "stop";
341
410
  }
342
411
  export interface DocumentText {
@@ -369,8 +438,8 @@ export interface SessionView {
369
438
  messages: Message[];
370
439
  /** 正在生成、还没写完的回复。 */
371
440
  draft?: AssistantMessage;
372
- approval?: ApprovalRequest;
373
- /** 排在 `approval` 后面还在等的审批张数,没有时不带。 */
441
+ approval?: PendingRequest;
442
+ /** 排在 `approval` 后面还在等的张数,没有时不带。 */
374
443
  queuedApprovals?: number;
375
444
  /** 正在执行的工具调用到目前为止的输出。 */
376
445
  progress: Record<string, string>;
@@ -384,8 +453,8 @@ export interface SessionView {
384
453
  compacting: boolean;
385
454
  /** 排着的几条,先排的在前,见 `QueuedMessage`。 */
386
455
  queue: QueuedMessage[];
387
- /** 这个子会话(侧边聊天)交给父会话的那几件事,先交的在前,见 `HandedTask`。没交过时没有。 */
388
- tasks?: HandedTask[];
456
+ /** 这个子会话交给父会话的那几件事,先交的在前,见 `ChildTask`。没交过时没有。 */
457
+ tasks?: ChildTask[];
389
458
  /** 转录里那几行压缩:自动的(定下要压以后)和人敲的 `/compact`,连同进行中的那一次,旧的在前。 */
390
459
  compactionRuns: CompactionRun[];
391
460
  }
@@ -444,6 +513,8 @@ export interface PluginStatus {
444
513
  builtin?: boolean;
445
514
  /** 等待中时,还没有插件提供的那几项服务。 */
446
515
  waitingFor?: string[];
516
+ /** 插件声明了要看全部会话的运行观察(`Plugin.observe`)。 */
517
+ observe?: "all";
447
518
  }
448
519
  export type AgentEvent = {
449
520
  type: "fileRemoved";
@@ -458,7 +529,10 @@ export type AgentEvent = {
458
529
  type: "sessions";
459
530
  sessions: SessionSummary[];
460
531
  }
461
- /** 一轮及其后台工作全部收尾;系统通知不能把中间停顿说成完成。 */
532
+ /**
533
+ * 一轮及其后台工作全部收尾(同 `SessionSummary.background` 落下);系统通知不能把中间停顿说成完成。会话起着不退出的后台任务、
534
+ * 约着还没触发的唤醒时不发。
535
+ */
462
536
  | {
463
537
  type: "idle";
464
538
  sessionId: string;
@@ -508,7 +582,7 @@ export type AgentEvent = {
508
582
  | {
509
583
  type: "approval";
510
584
  sessionId: string;
511
- approval: ApprovalRequest | undefined;
585
+ approval: PendingRequest | undefined;
512
586
  queuedApprovals?: number;
513
587
  }
514
588
  /** 这一轮的连接岔子变了(开始重试、又重试一次、收场):全部的最新快照。 */
@@ -535,7 +609,7 @@ export type AgentEvent = {
535
609
  | {
536
610
  type: "tasks";
537
611
  sessionId: string;
538
- tasks: HandedTask[];
612
+ tasks: ChildTask[];
539
613
  }
540
614
  /** 这一轮出了模型调用以外的问题(如没配置模型、钩子出错)。 */
541
615
  /** 有 `level` 的进提示条(插件经 `ctx.notice` 说的、循环在压缩或重发);没有的是这一轮出了错,写在输入框上面。 */
@@ -549,13 +623,18 @@ export type AgentEvent = {
549
623
  | {
550
624
  type: "plugins";
551
625
  plugins: PluginStatus[];
626
+ }
627
+ /** 工具表变了(插件装卸、启停,插件运行中登记、撤下工具):全部生效的工具,同 `listTools`。 */
628
+ | {
629
+ type: "tools";
630
+ tools: RegisteredToolInfo[];
552
631
  };
553
632
  /**
554
- * 上下文窗口按来源拆开:对话消息、工具表(MCP 服务的工具 `mcpTools` 单独一段,其余是 `tools`)、系统提示词,
555
- * 以及系统提示词里单独列出的技能目录(plume.skills 的 `skills` 段)、项目指令(plume.prompt 的 `projectInstructions` 段)
556
- * 和项目记忆(`projectMemory` 段)。
633
+ * 上下文窗口按来源拆开:对话消息、工具表(`ToolDefinition.category` 是 `mcp` 的工具 `mcpTools` 单独一段,其余是 `tools`)、系统提示词,
634
+ * 以及系统提示词里单独列出的技能目录(`PromptSection.category` 是 `skills` 的段)、项目里的指令文件(`conventions` 的段)
635
+ * 和记忆(`memory` 的段)。
557
636
  */
558
- export type ContextSegmentKey = "messages" | "tools" | "mcpTools" | "skills" | "systemPrompt" | "projectInstructions" | "projectMemory";
637
+ export type ContextSegmentKey = "messages" | "tools" | "mcpTools" | "skills" | "systemPrompt" | "conventions" | "memory";
559
638
  export interface ContextBreakdown {
560
639
  /** 下一轮会用的模型的上下文窗口。 */
561
640
  limit: number;
@@ -569,7 +648,7 @@ export interface ContextBreakdown {
569
648
  tokens: number;
570
649
  }>;
571
650
  /**
572
- * 项目指令和项目记忆两段里来自各文件的部分(插件经 `PromptSection.files` 标的),先项目指令、后项目记忆,各按段里的顺序。
651
+ * 项目指令(`conventions`)和记忆两段里来自各文件的部分(插件经 `PromptSection.files` 标的),先项目指令、后记忆,各按段里的顺序。
573
652
  * 和各段一样按字数估,按各自所在的段缩放;包在文件外面的说明文字不算哪个文件。没有标来源时是空的。
574
653
  */
575
654
  memoryFiles: Array<{
@@ -593,6 +672,10 @@ export interface AgentService {
593
672
  /** 整体替换星标的模型。 */
594
673
  setFavoriteModels(models: ModelRef[]): Promise<void>;
595
674
  setPermissionMode(mode: PermissionMode): Promise<void>;
675
+ /** 撤销一条「以后不再问」(同一个名下、同一个 subject),下次再问时重新弹卡;没有这一条时什么都不做。 */
676
+ revokeAlwaysAllow(grant: AlwaysAllow): Promise<void>;
677
+ /** 撤销全部「以后不再问」。 */
678
+ clearAlwaysAllow(): Promise<void>;
596
679
  /** 设新会话的思考档位,已有的会话不变。 */
597
680
  setDefaultThinking(thinking: ThinkingLevel): Promise<void>;
598
681
  /** 整体替换重试策略,存之前按 `normalizeRetryPolicy` 收好范围。 */
@@ -643,6 +726,11 @@ export interface AgentService {
643
726
  renameSession(sessionId: string, title: string): Promise<void>;
644
727
  /** 归档或取消归档。归档的会话照样能打开、接着聊。 */
645
728
  setSessionArchived(sessionId: string, archived: boolean): Promise<void>;
729
+ /**
730
+ * 停止代管(会话菜单):去掉 `SessionSummary.steward`,那个插件从此不能往里转话、不能替人答卡,之后再调就抛错;
731
+ * 它转进来、还排着没发出的几条一起从队里拿掉。正在跑的那一轮不停。没有代管时什么都不做。
732
+ */
733
+ revokeSteward(sessionId: string): Promise<void>;
646
734
  /** 置顶和手动未读都不改变最后活动时间。 */
647
735
  setSessionPinned(sessionId: string, pinned: boolean): Promise<void>;
648
736
  setSessionUnread(sessionId: string, unread: boolean): Promise<void>;
@@ -680,21 +768,21 @@ export interface AgentService {
680
768
  /** 拖动、↑↓ 改先后:`id` 那一条挪到 `targetId` 前面或后面。两条有一条不在队里时什么都不做。 */
681
769
  moveQueued(sessionId: string, id: string, targetId: string, placement: "before" | "after"): Promise<void>;
682
770
  /**
683
- * 人在子会话(侧边聊天)的卡片上点了「交给主会话」:把这件事排进 `sessionId` 这个子会话的父会话,和插件的
684
- * `ParentSession.enqueue` 同一条路(标着来自侧边聊天)。只认父会话,不是子会话时报错。
771
+ * 人在子会话的卡片上点了「交给主会话」:把这件事排进 `sessionId` 这个子会话的父会话,和插件的
772
+ * `ParentSession.enqueue` 同一条路(标着来自建这个子会话的插件)。只认父会话,不是子会话时报错。
685
773
  */
686
774
  queueTask(sessionId: string, instruction: string): Promise<void>;
687
775
  /**
688
- * 回执条上的「重试」「继续」:子会话 `sessionId` 交过去、失败了或随父会话停下的那件事重新排进父会话队尾,和第一次交过去一样。
776
+ * 「重试」「继续」:子会话 `sessionId` 交过去、失败了或随父会话停下的那件事(`ChildTask`)重新排进父会话队尾,和第一次交过去一样。
689
777
  * 别的状态不动,返回排上没有。
690
778
  */
691
779
  retryTask(sessionId: string, taskId: string): Promise<boolean>;
692
- /** 回执条上的「移除」:收了场的那件从列表里拿掉,父会话的转录不动。还排着、在跑的不动,返回拿掉没有。 */
780
+ /** 「移除」:收了场的那件(`ChildTask`)从列表里拿掉,父会话的转录不动。还排着、在跑的不动,返回拿掉没有。 */
693
781
  dismissTask(sessionId: string, taskId: string): Promise<boolean>;
694
782
  /**
695
783
  * 编辑重发:第 `index` 条(人发的消息)连同它之后的全部截掉,拿 `message` 重新开始一轮。截完就返回,这一轮经 watch 推送。
696
784
  * 正在跑时报错。图片块可以只带 `media`(会话里存着的那张),不用把像素再传一遍。
697
- * `files` 为真时先把截掉的那段改过的文件退回去(钩子 session.rewind),退不回去就报错,对话不截、也不重发。
785
+ * `files` 为真时先把截掉的那段改过的文件退回去(本体按截掉那段结果里的 `ToolResultMessage.changes` 退),退不回去就报错,对话不截、也不重发。
698
786
  * 插件的子会话也能编辑重发(插件经 `ConversationProps.reask` 同意了),但不带 `files`;撤回、演算、分叉仍然只给人自己的会话。
699
787
  * `timestamp` 是界面上那一条的时间戳:序号对不上时按它认回那一条,认不出就报错,不截错地方。
700
788
  */
@@ -707,7 +795,7 @@ export interface AgentService {
707
795
  files?: boolean;
708
796
  timestamp?: number;
709
797
  }): Promise<void>;
710
- /** 撤回、编辑重发第 `index` 条会截掉的那段改过哪些文件、能不能一起退回,只演算,不写(钩子 session.rewind)。`timestamp` 同 `editMessage`。 */
798
+ /** 撤回、编辑重发第 `index` 条会截掉的那段改过哪些文件、能不能一起退回,只演算,不写。`timestamp` 同 `editMessage`。 */
711
799
  revertPlan(sessionId: string, index: number, options?: {
712
800
  timestamp?: number;
713
801
  }): Promise<RevertPlan>;
@@ -741,12 +829,20 @@ export interface AgentService {
741
829
  */
742
830
  locateFiles(sessionId: string, relative: string): Promise<string[]>;
743
831
  stop(sessionId: string): void;
832
+ /** 答弹着的那张卡。对 `HumanRequest` 只认 `"reject"`(人不答),别的不理。 */
744
833
  respond(sessionId: string, toolCallId: string, decision: ApprovalDecision): void;
834
+ /** 交回弹着的那张 `HumanRequest` 要的数据。卡已经不在了(停下、过期、答过)时不理。`value` 不可信,由工具自己核。 */
835
+ answer(sessionId: string, toolCallId: string, value: unknown): void;
745
836
  /** 插件经 `ctx.commands` 供的全部命令,给输入框的 `/` 菜单。同名的只留先加载的插件那条;哪个插件列不出来就跳过它。 */
746
837
  listCommands(context: CommandContext): Promise<CommandEntry[]>;
747
838
  /** 让供它的插件把 `/name rest` 展开;插件已经撤下了或不再认这个名字时报错。 */
748
839
  expandCommand(plugin: string, name: string, rest: string, context: CommandContext): Promise<CommandResult>;
749
840
  listPlugins(): PluginStatus[];
841
+ /**
842
+ * 工具表里生效的工具和它们的声明(种类、类别),同 `ctx.tools.list()`。界面按工具的种类(`ToolDefinition.kind`)给转录里的说法、归类、
843
+ * 图标和回复里的文件签,不认工具名;变了推 `tools` 事件。
844
+ */
845
+ listTools(): RegisteredToolInfo[];
750
846
  /** 启用或停用一个插件(按插件名),写进 plugins.json,立即生效。 */
751
847
  setPluginEnabled(name: string, enabled: boolean): Promise<PluginStatus[]>;
752
848
  /** 界面语言变了:agent 进程之后给人看的报错、提示用这种语言。发给模型的文字不跟着变。 */
@@ -780,6 +876,8 @@ export interface ProjectLayerView {
780
876
  error?: string;
781
877
  problem?: ConfigProblem;
782
878
  refused: string[];
879
+ /** 写了、但会放宽权限所以不认的键(`alwaysAllow`、比本机宽的 `permissionMode`):仓库里的配置只能收紧权限。 */
880
+ loosened: string[];
783
881
  overrides: LayerOverride[];
784
882
  }
785
883
  export type FileOperation = {