@capybara-hq/kernel 0.1.0-rc.2 → 0.1.0-rc.3

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 (133) hide show
  1. package/dist/agents/agent-lookup.js +10 -2
  2. package/dist/agents/agents.service.js +19 -6
  3. package/dist/agents/definition.d.ts +31 -8
  4. package/dist/agents/definition.js +193 -17
  5. package/dist/agents/templates.js +19 -4
  6. package/dist/catalog/catalog-content.js +4 -1
  7. package/dist/catalog/claude/agents.js +13 -10
  8. package/dist/catalog/marketplaces.service.d.ts +1 -1
  9. package/dist/channels/cards.d.ts +8 -2
  10. package/dist/channels/cards.js +16 -8
  11. package/dist/config/schema.js +2 -2
  12. package/dist/conversations/artifacts.d.ts +1 -1
  13. package/dist/conversations/artifacts.js +1 -1
  14. package/dist/conversations/event-log.d.ts +11 -0
  15. package/dist/conversations/event-log.js +19 -0
  16. package/dist/conversations/events.d.ts +23 -3
  17. package/dist/conversations/events.js +26 -14
  18. package/dist/db/kernel-schema.d.ts +2 -2
  19. package/dist/evals/recorded-tools.d.ts +27 -1
  20. package/dist/evals/recorded-tools.js +85 -10
  21. package/dist/evals/run-replay.d.ts +5 -0
  22. package/dist/evals/run-replay.js +19 -5
  23. package/dist/evals/trajectory.d.ts +2 -2
  24. package/dist/identity/settings.js +1 -1
  25. package/dist/lifecycle/kernel-lifecycle.js +5 -0
  26. package/dist/orgs/settings.service.d.ts +2 -2
  27. package/dist/orgs/settings.service.js +2 -2
  28. package/dist/runtime/context/build.d.ts +6 -6
  29. package/dist/runtime/context/build.js +18 -8
  30. package/dist/runtime/context/claude-tools.d.ts +2 -1
  31. package/dist/runtime/context/claude-tools.js +9 -10
  32. package/dist/runtime/context/legacy-text.d.ts +22 -0
  33. package/dist/runtime/context/legacy-text.js +37 -0
  34. package/dist/runtime/context/skills.d.ts +29 -11
  35. package/dist/runtime/context/skills.js +37 -30
  36. package/dist/runtime/loop.d.ts +1 -1
  37. package/dist/runtime/model-call.js +1 -1
  38. package/dist/runtime/run-finish.d.ts +1 -1
  39. package/dist/runtime/run-queue.d.ts +3 -3
  40. package/dist/runtime/run-queue.js +2 -2
  41. package/dist/runtime/run-worker.d.ts +2 -2
  42. package/dist/runtime/run-worker.js +4 -4
  43. package/dist/runtime/subagents/definition.d.ts +1 -6
  44. package/dist/runtime/subagents/definition.js +11 -12
  45. package/dist/runtime/subagents/dispatch.d.ts +2 -2
  46. package/dist/runtime/subagents/dispatch.js +2 -2
  47. package/dist/runtime/subagents/org-agents.js +2 -1
  48. package/dist/runtime/subagents/roster.d.ts +1 -1
  49. package/dist/runtime/subagents/roster.js +1 -1
  50. package/dist/runtime/subagents/skill-fork.d.ts +2 -2
  51. package/dist/runtime/subagents/skill-fork.js +2 -2
  52. package/dist/runtime/subagents/subagents.service.d.ts +3 -3
  53. package/dist/runtime/subagents/subagents.service.js +3 -3
  54. package/dist/runtime/waits.d.ts +5 -5
  55. package/dist/runtime/waits.js +5 -5
  56. package/dist/skills/catalog.d.ts +4 -4
  57. package/dist/skills/catalog.js +4 -4
  58. package/dist/skills/changes.service.d.ts +2 -2
  59. package/dist/skills/changes.service.js +5 -4
  60. package/dist/skills/invocation-keys.d.ts +1 -1
  61. package/dist/skills/invocation-keys.js +1 -1
  62. package/dist/skills/invoke.d.ts +1 -1
  63. package/dist/skills/invoke.js +5 -4
  64. package/dist/tools/approvals.service.d.ts +6 -0
  65. package/dist/tools/approvals.service.js +19 -1
  66. package/dist/tools/builtin-names.d.ts +73 -0
  67. package/dist/tools/builtin-names.js +116 -0
  68. package/dist/tools/builtins/agent.d.ts +7 -7
  69. package/dist/tools/builtins/agent.js +7 -11
  70. package/dist/tools/builtins/ask-user.d.ts +1 -1
  71. package/dist/tools/builtins/ask-user.js +2 -2
  72. package/dist/tools/builtins/builtin-tools.d.ts +2 -2
  73. package/dist/tools/builtins/builtin-tools.js +2 -2
  74. package/dist/tools/builtins/read-tool-output.d.ts +1 -1
  75. package/dist/tools/builtins/read-tool-output.js +2 -2
  76. package/dist/tools/builtins/sandbox/bash.d.ts +5 -5
  77. package/dist/tools/builtins/sandbox/bash.js +31 -28
  78. package/dist/tools/builtins/sandbox/common.d.ts +3 -3
  79. package/dist/tools/builtins/sandbox/common.js +2 -2
  80. package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
  81. package/dist/tools/builtins/sandbox/deliver.js +11 -11
  82. package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
  83. package/dist/tools/builtins/sandbox/edit.js +41 -30
  84. package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
  85. package/dist/tools/builtins/sandbox/file-state.js +34 -0
  86. package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
  87. package/dist/tools/builtins/sandbox/glob-match.js +1 -1
  88. package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
  89. package/dist/tools/builtins/sandbox/glob.js +4 -4
  90. package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
  91. package/dist/tools/builtins/sandbox/grep.js +147 -30
  92. package/dist/tools/builtins/sandbox/index.d.ts +1 -1
  93. package/dist/tools/builtins/sandbox/index.js +1 -1
  94. package/dist/tools/builtins/sandbox/read.d.ts +2 -2
  95. package/dist/tools/builtins/sandbox/read.js +21 -14
  96. package/dist/tools/builtins/sandbox/write.d.ts +1 -1
  97. package/dist/tools/builtins/sandbox/write.js +37 -33
  98. package/dist/tools/builtins/skill-load.d.ts +3 -3
  99. package/dist/tools/builtins/skill-load.js +32 -16
  100. package/dist/tools/builtins/skill-write.d.ts +4 -4
  101. package/dist/tools/builtins/skill-write.js +10 -10
  102. package/dist/tools/builtins/tool-search.d.ts +13 -9
  103. package/dist/tools/builtins/tool-search.js +25 -14
  104. package/dist/tools/connectors.service.js +4 -4
  105. package/dist/tools/display-names.d.ts +9 -0
  106. package/dist/tools/display-names.js +24 -0
  107. package/dist/tools/drift.js +5 -5
  108. package/dist/tools/elicitation.js +1 -1
  109. package/dist/tools/info.d.ts +20 -4
  110. package/dist/tools/info.js +55 -32
  111. package/dist/tools/legacy-data.d.ts +34 -0
  112. package/dist/tools/legacy-data.js +76 -0
  113. package/dist/tools/legacy-keys.d.ts +51 -0
  114. package/dist/tools/legacy-keys.js +115 -0
  115. package/dist/tools/naming.d.ts +1 -5
  116. package/dist/tools/naming.js +1 -7
  117. package/dist/tools/outputs.js +1 -1
  118. package/dist/tools/pipeline.d.ts +13 -7
  119. package/dist/tools/pipeline.js +97 -39
  120. package/dist/tools/policy.d.ts +1 -14
  121. package/dist/tools/policy.js +13 -19
  122. package/dist/tools/questions.controller.d.ts +1 -1
  123. package/dist/tools/questions.controller.js +1 -1
  124. package/dist/tools/questions.service.d.ts +1 -1
  125. package/dist/tools/registry.d.ts +16 -21
  126. package/dist/tools/registry.js +29 -37
  127. package/dist/tools/resources.d.ts +1 -1
  128. package/dist/tools/resources.js +1 -1
  129. package/dist/tools/tools.module.js +1 -1
  130. package/package.json +2 -2
  131. package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
  132. package/dist/tools/builtins/sandbox/names.d.ts +0 -7
  133. package/dist/tools/builtins/sandbox/names.js +0 -20
@@ -7,13 +7,8 @@ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
7
7
  import { KernelDb } from "../infra/database/kernel-db.js";
8
8
  import type { OrgRole } from "../orgs/roles.js";
9
9
  import { BuiltinTools } from "./builtins/builtin-tools.js";
10
+ export { BUILTIN_TOOL_NAMES } from "./builtin-names.js";
10
11
  export { canonicalJson } from "./canonical-json.js";
11
- /**
12
- * 内核内置工具的名字(Task 10 / Task 19 实现;M5 T8 加沙箱工具)。保存 agent 时内置工具引用按这份清单校验,
13
- * 不依赖当前是否已注册,避免实现落地顺序影响已保存的定义;运行时未注册的照常报不可用。
14
- * `skill_write`(M5 T15)由 agent 定义显式引用才可用(负责人决定 agent 能否改自己的技能)。
15
- */
16
- export declare const BUILTIN_TOOL_NAMES: readonly ["ask_user", "read_tool_output", "tool_search", "skill_load", "skill_write", "sandbox_bash", "sandbox_read", "sandbox_write", "sandbox_edit", "sandbox_glob", "sandbox_grep", "sandbox_deliver", "agent"];
17
12
  /** 保存 agent 时引用校验的问题(`path` 为 `tools.<i>`)。 */
18
13
  export interface ToolRefIssue {
19
14
  path: string;
@@ -21,7 +16,7 @@ export interface ToolRefIssue {
21
16
  }
22
17
  /**
23
18
  * 内置工具处理函数的上下文:在 SDK 的 `ToolCallContext` 之上,管线额外传入本步解析出的工具
24
- * (`tool_search` 据此搜索)。插件处理函数收到的仍是 `ToolCallContext`。
19
+ * (`ToolSearch` 据此搜索)。插件处理函数收到的仍是 `ToolCallContext`。
25
20
  */
26
21
  export interface BuiltinCallContext extends ToolCallContext {
27
22
  tools?: readonly ResolvedTool[];
@@ -39,7 +34,7 @@ export interface BuiltinCallContext extends ToolCallContext {
39
34
  sandboxConversationId?: string;
40
35
  }
41
36
  /**
42
- * 内置工具的结果:`ToolResult` 之外可带 `loadedTools`(`tool_search` 加载的工具 key,
37
+ * 内置工具的结果:`ToolResult` 之外可带 `loadedTools`(`ToolSearch` 加载的工具 key,
43
38
  * 管线写进 `tool_result.loadedTools`)。
44
39
  */
45
40
  export type BuiltinResult = (ToolResult & {
@@ -52,7 +47,7 @@ export type BuiltinResult = (ToolResult & {
52
47
  }) | InputRequired;
53
48
  /** 内置工具的处理函数(插件的 `ToolHandler` 可直接赋给它)。 */
54
49
  export type BuiltinHandler = (input: unknown, ctx: BuiltinCallContext) => Promise<BuiltinResult>;
55
- /** 内核内置工具(`ask_user` 等,Task 10 实现具体工具);key 就是 `spec.name`。 */
50
+ /** 内核内置工具(`AskUserQuestion` 等,Task 10 实现具体工具);key 就是 `spec.name`。 */
56
51
  export interface BuiltinTool {
57
52
  spec: ToolSpec;
58
53
  defaultPolicy: "allow" | "ask";
@@ -116,7 +111,7 @@ export interface ResolvedTool {
116
111
  */
117
112
  requiredRole?: OrgRole;
118
113
  }
119
- /** 不可用的引用:`ref` 尽量写成模型认识的工具 key(`docs__search`、`docs__*`),否则 `app:<id>`。 */
114
+ /** 不可用的引用:`ref` 尽量写成模型认识的工具 key(`mcp__docs__search`、`mcp__docs__*`),否则 `app:<id>`。 */
120
115
  export interface UnavailableTool {
121
116
  ref: string;
122
117
  reason: string;
@@ -148,7 +143,7 @@ export interface AgentTools {
148
143
  */
149
144
  hidden?: UnavailableTool[];
150
145
  /**
151
- * 本步是否启用 `tool_search`(spec 5.5):启用时 `tools` 末尾含 `tool_search`,构建请求时只放内置工具
146
+ * 本步是否启用 `ToolSearch`(spec 5.5):启用时 `tools` 末尾含 `ToolSearch`,构建请求时只放内置工具
152
147
  * 与已加载工具的完整定义,其余进目录。缺省视为未启用。
153
148
  */
154
149
  toolSearch?: boolean;
@@ -178,7 +173,7 @@ export declare class ToolRegistry {
178
173
  private readonly extensions;
179
174
  private readonly builtinTools;
180
175
  /**
181
- * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `tool_search` is never
176
+ * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `ToolSearch` is never
182
177
  * offered and the sub-agent limits are the schema defaults.
183
178
  */
184
179
  private readonly config?;
@@ -188,11 +183,11 @@ export declare class ToolRegistry {
188
183
  /** The installed connectors (`connector@1`, from the extension registry). */
189
184
  extensions: Pick<ExtensionRegistry, "list">, builtinTools: Pick<BuiltinTools, "list">,
190
185
  /**
191
- * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `tool_search` is never
186
+ * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `ToolSearch` is never
192
187
  * offered and the sub-agent limits are the schema defaults.
193
188
  */
194
189
  config?: Pick<KernelConfigService, "get"> | undefined);
195
- /** The kernel's builtin tools (`tool_search` is added by {@link forAgent}, not by definitions). */
190
+ /** The kernel's builtin tools (`ToolSearch` is added by {@link forAgent}, not by definitions). */
196
191
  private get builtins();
197
192
  /** `host.list("connector")` 中 `kind` 匹配的第一个实现;未安装返回 undefined。 */
198
193
  findConnector(kind: string): Connector | undefined;
@@ -228,23 +223,23 @@ export declare class ToolRegistry {
228
223
  issues: ToolRefIssue[];
229
224
  }>;
230
225
  /**
231
- * 解析 agent 引用的工具(见类注释),再判定是否启用 `tool_search`:组织设置 `toolSearch === "on"`,且
226
+ * 解析 agent 引用的工具(见类注释),再判定是否启用 `ToolSearch`:组织设置 `toolSearch === "on"`,且
232
227
  * 可用工具数 > `tools.searchThresholdTools` 或其定义的估算 token > `tools.searchThresholdTokens`。
233
- * 启用时把内置 `tool_search` 追加到末尾。定义里显式引用的 `tool_search` 忽略(由这里按设置管理)。
234
- * `skillLoad`:对话日志里有技能目录快照(`skills_catalog`,Ruling M17)→ 加入内置 `skill_load`(始终完整加载);
235
- * 定义里显式引用的 `skill_load` 同样忽略。
228
+ * 启用时把内置 `ToolSearch` 追加到末尾。定义里显式引用的 `ToolSearch` 忽略(由这里按设置管理)。
229
+ * `skillLoad`:对话日志里有技能目录快照(`skills_catalog`,Ruling M17)→ 加入内置 `Skill`(始终完整加载);
230
+ * 定义里显式引用的 `Skill` 同样忽略。
236
231
  */
237
232
  forAgent(orgId: string, def: {
238
233
  tools: readonly AgentToolRef[];
239
234
  skillLoad?: boolean;
240
- /** The definition's sub-agent roster source: the `agent` tool's description lists it. */
235
+ /** The definition's sub-agent roster source: the `Agent` tool's description lists it. */
241
236
  subagents?: AgentDefinition["subagents"];
242
237
  }): Promise<AgentTools>;
243
- /** 是否启用 `tool_search`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
238
+ /** 是否启用 `ToolSearch`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
244
239
  private searchActive;
245
240
  private resolve;
246
241
  /**
247
- * `subagents.maxConcurrentPerRun` with the organization's override, for the `agent` tool's
242
+ * `subagents.maxConcurrentPerRun` with the organization's override, for the `Agent` tool's
248
243
  * description (§4.3). A change of the limit changes the tool's spec hash; replays read the
249
244
  * definition logged by hash, so no drift reason is involved. Without config (some tests): the
250
245
  * schema default.
@@ -8,6 +8,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { createHash } from "node:crypto";
11
+ import { connectorToolKey, extensionToolKey } from "@capybara-hq/plugin-sdk";
11
12
  import { Inject, Injectable } from "@nestjs/common";
12
13
  import { ExtensionHost } from "../extensions/host.js";
13
14
  import { ExtensionRegistry } from "../extensions/registry.js";
@@ -17,27 +18,14 @@ import { readOrgSettings } from "../orgs/settings.service.js";
17
18
  import { estimateTokens } from "../runtime/context/tokens.js";
18
19
  import { SUBAGENT_LIMIT_DEFAULTS, subagentLimits } from "../runtime/subagents/limits.js";
19
20
  import { resolveRoster } from "../runtime/subagents/roster.js";
20
- import { AGENT_TOOL, agentToolSpec } from "./builtins/agent.js";
21
+ import { AGENT_TOOL, BUILTIN_TOOL_NAMES, SKILL_LOAD, TOOL_SEARCH } from "./builtin-names.js";
22
+ import { agentToolSpec } from "./builtins/agent.js";
21
23
  import { BuiltinTools } from "./builtins/builtin-tools.js";
22
- import { SANDBOX_TOOL_NAMES } from "./builtins/sandbox/names.js";
23
24
  import { canonicalJson } from "./canonical-json.js";
24
- import { toolKey, toolKeyIssue } from "./naming.js";
25
+ import { legacyKeySaveText } from "./legacy-keys.js";
26
+ import { toolKeyIssue } from "./naming.js";
27
+ export { BUILTIN_TOOL_NAMES } from "./builtin-names.js";
25
28
  export { canonicalJson } from "./canonical-json.js";
26
- /**
27
- * 内核内置工具的名字(Task 10 / Task 19 实现;M5 T8 加沙箱工具)。保存 agent 时内置工具引用按这份清单校验,
28
- * 不依赖当前是否已注册,避免实现落地顺序影响已保存的定义;运行时未注册的照常报不可用。
29
- * `skill_write`(M5 T15)由 agent 定义显式引用才可用(负责人决定 agent 能否改自己的技能)。
30
- */
31
- export const BUILTIN_TOOL_NAMES = [
32
- "ask_user",
33
- "read_tool_output",
34
- "tool_search",
35
- "skill_load",
36
- "skill_write",
37
- ...SANDBOX_TOOL_NAMES,
38
- // phase C: dispatches sub-agents; its description is built per agent from the roster
39
- "agent",
40
- ];
41
29
  /** The first 16 hex digits of sha256(canonicalJson(v)): a fingerprint independent of key order. */
42
30
  export function contentHash(v) {
43
31
  return createHash("sha256").update(canonicalJson(v)).digest("hex").slice(0, 16);
@@ -68,10 +56,8 @@ export function specHash(spec) {
68
56
  export function policyFromAnnotations(a) {
69
57
  return a?.readOnlyHint === true ? "allow" : "ask";
70
58
  }
71
- /** 内置 `tool_search` 的名字(由注册表按组织设置与阈值自动加入,不进 agent 定义)。 */
72
- const TOOL_SEARCH = "tool_search";
73
- /** 内置 `skill_load` 的名字(对话日志里有技能目录快照时由注册表自动加入,不进 agent 定义)。 */
74
- const SKILL_LOAD = "skill_load";
59
+ // `ToolSearch` is added by the registry by org setting and threshold, `Skill` when the log has a
60
+ // skills catalog snapshot; neither comes from agent definitions (`./builtin-names.ts`).
75
61
  /** 工具定义(模型可见三项)的估算 token 数(与请求估算同一口径)。 */
76
62
  function estimateToolTokens(tools) {
77
63
  return estimateTokens([], [], tools.map((t) => ({
@@ -114,7 +100,7 @@ let ToolRegistry = class ToolRegistry {
114
100
  /** The installed connectors (`connector@1`, from the extension registry). */
115
101
  extensions, builtinTools,
116
102
  /**
117
- * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `tool_search` is never
103
+ * `tools.searchThreshold*` and the sub-agent limits. Absent (some tests): `ToolSearch` is never
118
104
  * offered and the sub-agent limits are the schema defaults.
119
105
  */
120
106
  config) {
@@ -124,7 +110,7 @@ let ToolRegistry = class ToolRegistry {
124
110
  this.builtinTools = builtinTools;
125
111
  this.config = config;
126
112
  }
127
- /** The kernel's builtin tools (`tool_search` is added by {@link forAgent}, not by definitions). */
113
+ /** The kernel's builtin tools (`ToolSearch` is added by {@link forAgent}, not by definitions). */
128
114
  get builtins() {
129
115
  return this.builtinTools.list();
130
116
  }
@@ -205,13 +191,19 @@ let ToolRegistry = class ToolRegistry {
205
191
  const path = `tools.${i}`;
206
192
  if (ref.kind === "builtin") {
207
193
  if (!BUILTIN_TOOL_NAMES.includes(ref.name)) {
208
- issues.push({ path, message: `Built-in tool "${ref.name}" does not exist` });
194
+ const renamed = legacyKeySaveText(ref.name);
195
+ issues.push({
196
+ path,
197
+ message: renamed === null
198
+ ? `Built-in tool "${ref.name}" does not exist`
199
+ : `Built-in tool ${renamed}`,
200
+ });
209
201
  return;
210
202
  }
211
203
  claim(path, ref.name, `builtin:${ref.name}`);
212
204
  }
213
205
  else if (ref.kind === "plugin") {
214
- const key = toolKey(ref.plugin, ref.name);
206
+ const key = extensionToolKey(ref.plugin, ref.name);
215
207
  if (!pluginTools.some((x) => x.plugin === ref.plugin && x.spec.name === ref.name)) {
216
208
  issues.push({ path, message: `Plugin tool "${key}" does not exist` });
217
209
  return;
@@ -238,18 +230,18 @@ let ToolRegistry = class ToolRegistry {
238
230
  });
239
231
  continue;
240
232
  }
241
- claim(path, toolKey(app.slug, name), `connector:${app.id}:${name}`);
233
+ claim(path, connectorToolKey(app.slug, name), `connector:${app.id}:${name}`);
242
234
  }
243
235
  }
244
236
  });
245
237
  return { keys: new Set(owner.keys()), issues };
246
238
  }
247
239
  /**
248
- * 解析 agent 引用的工具(见类注释),再判定是否启用 `tool_search`:组织设置 `toolSearch === "on"`,且
240
+ * 解析 agent 引用的工具(见类注释),再判定是否启用 `ToolSearch`:组织设置 `toolSearch === "on"`,且
249
241
  * 可用工具数 > `tools.searchThresholdTools` 或其定义的估算 token > `tools.searchThresholdTokens`。
250
- * 启用时把内置 `tool_search` 追加到末尾。定义里显式引用的 `tool_search` 忽略(由这里按设置管理)。
251
- * `skillLoad`:对话日志里有技能目录快照(`skills_catalog`,Ruling M17)→ 加入内置 `skill_load`(始终完整加载);
252
- * 定义里显式引用的 `skill_load` 同样忽略。
242
+ * 启用时把内置 `ToolSearch` 追加到末尾。定义里显式引用的 `ToolSearch` 忽略(由这里按设置管理)。
243
+ * `skillLoad`:对话日志里有技能目录快照(`skills_catalog`,Ruling M17)→ 加入内置 `Skill`(始终完整加载);
244
+ * 定义里显式引用的 `Skill` 同样忽略。
253
245
  */
254
246
  async forAgent(orgId, def) {
255
247
  const { tools, unavailable, hidden } = await this.resolve(orgId, def);
@@ -277,7 +269,7 @@ let ToolRegistry = class ToolRegistry {
277
269
  }
278
270
  return { tools, unavailable, hidden, toolSearch };
279
271
  }
280
- /** 是否启用 `tool_search`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
272
+ /** 是否启用 `ToolSearch`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
281
273
  async searchActive(orgId, tools) {
282
274
  const cfg = this.config?.get().tools;
283
275
  if (!cfg || !this.builtins.some((b) => b.spec.name === TOOL_SEARCH))
@@ -329,7 +321,7 @@ let ToolRegistry = class ToolRegistry {
329
321
  unavailable.push({ ref: ref.name, reason: why });
330
322
  continue;
331
323
  }
332
- // `agent`: the description lists this definition's roster (sub-agents spec §4.3); with an
324
+ // `Agent`: the description lists this definition's roster (sub-agents spec §4.3); with an
333
325
  // empty roster there is nothing to dispatch, so the tool is not offered at all
334
326
  let spec = b.spec;
335
327
  if (b.spec.name === AGENT_TOOL) {
@@ -349,7 +341,7 @@ let ToolRegistry = class ToolRegistry {
349
341
  });
350
342
  }
351
343
  else if (ref.kind === "plugin") {
352
- const key = toolKey(ref.plugin, ref.name);
344
+ const key = extensionToolKey(ref.plugin, ref.name);
353
345
  const t = pluginTools.find((x) => x.plugin === ref.plugin && x.spec.name === ref.name);
354
346
  if (!t) {
355
347
  unavailable.push({ ref: key, reason: "Plugin tool does not exist" });
@@ -381,7 +373,7 @@ let ToolRegistry = class ToolRegistry {
381
373
  unavailable.push({ ref: `app:${ref.connectorId}`, reason: "App does not exist" });
382
374
  continue;
383
375
  }
384
- const all = toolKey(app.slug, "*");
376
+ const all = connectorToolKey(app.slug, "*");
385
377
  if (app.deleted) {
386
378
  unavailable.push({ ref: all, reason: `App ${app.slug} was deleted` });
387
379
  continue;
@@ -403,7 +395,7 @@ let ToolRegistry = class ToolRegistry {
403
395
  // 「all」里未启用的工具不算 agent 点名要的,不进 unavailable(不提醒),只记进 hidden 备查
404
396
  const notEnabled = ref.tools === "all" ? hidden : unavailable;
405
397
  for (const t of wanted) {
406
- const key = toolKey(app.slug, t.name);
398
+ const key = connectorToolKey(app.slug, t.name);
407
399
  // 远端已不再提供(漂移检测置停用并标记,见 drift.ts):与「没有这一行」同一个事实
408
400
  if ("missing" in t || t.gone) {
409
401
  notEnabled.push({ ref: key, reason: `Tool no longer exists in app ${app.slug}` });
@@ -440,7 +432,7 @@ let ToolRegistry = class ToolRegistry {
440
432
  return { tools, unavailable, hidden };
441
433
  }
442
434
  /**
443
- * `subagents.maxConcurrentPerRun` with the organization's override, for the `agent` tool's
435
+ * `subagents.maxConcurrentPerRun` with the organization's override, for the `Agent` tool's
444
436
  * description (§4.3). A change of the limit changes the tool's spec hash; replays read the
445
437
  * definition logged by hash, so no drift reason is involved. Without config (some tests): the
446
438
  * schema default.
@@ -1,6 +1,6 @@
1
1
  import { type ConnectorSession, type ToolResult, type ToolSpec } from "@capybara-hq/plugin-sdk";
2
2
  /**
3
- * 连接器 resources 的两个内核工具(brief:`<connectorSlug>__list_resources`、`<connectorSlug>__read_resource {uri}`)。
3
+ * 连接器 resources 的两个内核工具(brief:`mcp__<slug>__list_resources`、`mcp__<slug>__read_resource {uri}`)。
4
4
  *
5
5
  * 设计:安装 / 刷新连接器时,连接器会话带 `listResources` 方法即视为支持 resources(不额外调用,免得多一次外连
6
6
  * 和多一种失败),内核把这两个工具作为 `source = 'resources'` 的行写进 `connector_tools`(迁移 0013)。这样它们和
@@ -1,6 +1,6 @@
1
1
  import { ToolError, } from "@capybara-hq/plugin-sdk";
2
2
  /**
3
- * 连接器 resources 的两个内核工具(brief:`<connectorSlug>__list_resources`、`<connectorSlug>__read_resource {uri}`)。
3
+ * 连接器 resources 的两个内核工具(brief:`mcp__<slug>__list_resources`、`mcp__<slug>__read_resource {uri}`)。
4
4
  *
5
5
  * 设计:安装 / 刷新连接器时,连接器会话带 `listResources` 方法即视为支持 resources(不额外调用,免得多一次外连
6
6
  * 和多一种失败),内核把这两个工具作为 `source = 'resources'` 的行写进 `connector_tools`(迁移 0013)。这样它们和
@@ -48,7 +48,7 @@ ToolsModule = __decorate([
48
48
  StorageModule,
49
49
  SkillsModule,
50
50
  OrgLockModule,
51
- // the `agent` tool's dispatch
51
+ // the `Agent` tool's dispatch
52
52
  SubagentsModule,
53
53
  // the builtin sandbox and skill tools run in the conversation's sandbox
54
54
  SandboxModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.2",
3
+ "version": "0.1.0-rc.3",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -64,7 +64,7 @@
64
64
  "yaml": "^2.9.1",
65
65
  "yauzl": "3.4.0",
66
66
  "zod": "^4.6.7",
67
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.0"
67
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.1"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",
@@ -0,0 +1,220 @@
1
+ -- Tool naming (spec docs/superpowers/specs/2026-10-29-tool-naming-claude-code-design.md §8.2):
2
+ -- the release candidates' tool keys in mutable state become the current keys. Builtins by the
3
+ -- frozen map of packages/kernel/src/tools/legacy-keys.ts (LEGACY_BUILTINS); an rc `<x>__<tool>`
4
+ -- key becomes `mcp__<x>__<tool>` when `x` is a connector of the row's org (or the row records its
5
+ -- connector) and `plugin__<x>__<tool>` when `x` is an extension of the deployment
6
+ -- (extension_installs, or extension_name_owners for config-declared ones); when both or neither,
7
+ -- the row stays and the kernel's startup notice `tools.legacy_key_unmapped` counts it.
8
+ -- Keys in the current format never match. A grant whose new key already exists for the same
9
+ -- owner is left as it is (the primary key). Decided approvals and answered questions are
10
+ -- history and stay. Logs and recordings are never rewritten (§8). Idempotent: a second run
11
+ -- matches nothing it could change.
12
+
13
+ -- capybara:allow-destructive rewrites rc tool keys in place (the old keys no longer name a tool; the kernel reads only the new ones)
14
+ UPDATE tool_grants g
15
+ SET tool_key = m.new_key
16
+ FROM (VALUES
17
+ ('sandbox_bash', 'Bash'), ('sandbox_read', 'Read'), ('sandbox_write', 'Write'),
18
+ ('sandbox_edit', 'Edit'), ('sandbox_glob', 'Glob'), ('sandbox_grep', 'Grep'),
19
+ ('sandbox_deliver', 'Deliver'), ('skill_load', 'Skill'), ('skill_write', 'SkillWrite'),
20
+ ('ask_user', 'AskUserQuestion'), ('read_tool_output', 'ReadToolOutput'),
21
+ ('tool_search', 'ToolSearch'), ('agent', 'Agent')
22
+ ) AS m(old_key, new_key)
23
+ WHERE g.tool_key = m.old_key
24
+ AND NOT EXISTS (
25
+ SELECT 1 FROM tool_grants x
26
+ WHERE x.org_id = g.org_id AND x.account_id = g.account_id AND x.agent_id = g.agent_id
27
+ AND x.tool_key = m.new_key
28
+ );
29
+
30
+ -- capybara:allow-destructive rewrites rc tool keys in place (the old keys no longer name a tool; the kernel reads only the new ones)
31
+ UPDATE tool_grants g
32
+ SET tool_key = CASE
33
+ WHEN g.connector_id IS NOT NULL OR (
34
+ EXISTS (SELECT 1 FROM connectors c
35
+ WHERE c.org_id = g.org_id AND c.deleted_at IS NULL
36
+ AND c.slug = split_part(g.tool_key, '__', 1))
37
+ AND split_part(g.tool_key, '__', 1) NOT IN (
38
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
39
+ ) THEN 'mcp__' || g.tool_key
40
+ ELSE 'plugin__' || g.tool_key
41
+ END
42
+ WHERE g.tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND g.tool_key !~ '^(mcp|plugin)__'
43
+ AND (
44
+ g.connector_id IS NOT NULL
45
+ OR (
46
+ EXISTS (SELECT 1 FROM connectors c
47
+ WHERE c.org_id = g.org_id AND c.deleted_at IS NULL
48
+ AND c.slug = split_part(g.tool_key, '__', 1))
49
+ ) <> (
50
+ split_part(g.tool_key, '__', 1) IN (
51
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
52
+ )
53
+ )
54
+ AND NOT EXISTS (
55
+ SELECT 1 FROM tool_grants x
56
+ WHERE x.org_id = g.org_id AND x.account_id = g.account_id AND x.agent_id = g.agent_id
57
+ AND x.tool_key IN ('mcp__' || g.tool_key, 'plugin__' || g.tool_key)
58
+ );
59
+
60
+ -- capybara:allow-destructive rewrites rc tool keys in place (the old keys no longer name a tool; the kernel reads only the new ones)
61
+ UPDATE conversation_tool_grants g
62
+ SET tool_key = m.new_key
63
+ FROM (VALUES
64
+ ('sandbox_bash', 'Bash'), ('sandbox_read', 'Read'), ('sandbox_write', 'Write'),
65
+ ('sandbox_edit', 'Edit'), ('sandbox_glob', 'Glob'), ('sandbox_grep', 'Grep'),
66
+ ('sandbox_deliver', 'Deliver'), ('skill_load', 'Skill'), ('skill_write', 'SkillWrite'),
67
+ ('ask_user', 'AskUserQuestion'), ('read_tool_output', 'ReadToolOutput'),
68
+ ('tool_search', 'ToolSearch'), ('agent', 'Agent')
69
+ ) AS m(old_key, new_key)
70
+ WHERE g.tool_key = m.old_key
71
+ AND NOT EXISTS (
72
+ SELECT 1 FROM conversation_tool_grants x
73
+ WHERE x.conversation_id = g.conversation_id AND x.tool_key = m.new_key
74
+ );
75
+
76
+ -- capybara:allow-destructive rewrites rc tool keys in place (the old keys no longer name a tool; the kernel reads only the new ones)
77
+ UPDATE conversation_tool_grants g
78
+ SET tool_key = CASE
79
+ WHEN g.connector_id IS NOT NULL OR (
80
+ EXISTS (SELECT 1 FROM connectors c
81
+ WHERE c.org_id = v.org_id AND c.deleted_at IS NULL
82
+ AND c.slug = split_part(g.tool_key, '__', 1))
83
+ AND split_part(g.tool_key, '__', 1) NOT IN (
84
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
85
+ ) THEN 'mcp__' || g.tool_key
86
+ ELSE 'plugin__' || g.tool_key
87
+ END
88
+ FROM conversations v
89
+ WHERE v.id = g.conversation_id
90
+ AND g.tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND g.tool_key !~ '^(mcp|plugin)__'
91
+ AND (
92
+ g.connector_id IS NOT NULL
93
+ OR (
94
+ EXISTS (SELECT 1 FROM connectors c
95
+ WHERE c.org_id = v.org_id AND c.deleted_at IS NULL
96
+ AND c.slug = split_part(g.tool_key, '__', 1))
97
+ ) <> (
98
+ split_part(g.tool_key, '__', 1) IN (
99
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
100
+ )
101
+ )
102
+ AND NOT EXISTS (
103
+ SELECT 1 FROM conversation_tool_grants x
104
+ WHERE x.conversation_id = g.conversation_id
105
+ AND x.tool_key IN ('mcp__' || g.tool_key, 'plugin__' || g.tool_key)
106
+ );
107
+
108
+ -- capybara:allow-destructive rewrites rc tool keys of pending approvals in place (resumed by run and call id, never by key)
109
+ UPDATE approvals a
110
+ SET tool_key = m.new_key
111
+ FROM (VALUES
112
+ ('sandbox_bash', 'Bash'), ('sandbox_read', 'Read'), ('sandbox_write', 'Write'),
113
+ ('sandbox_edit', 'Edit'), ('sandbox_glob', 'Glob'), ('sandbox_grep', 'Grep'),
114
+ ('sandbox_deliver', 'Deliver'), ('skill_load', 'Skill'), ('skill_write', 'SkillWrite'),
115
+ ('ask_user', 'AskUserQuestion'), ('read_tool_output', 'ReadToolOutput'),
116
+ ('tool_search', 'ToolSearch'), ('agent', 'Agent')
117
+ ) AS m(old_key, new_key)
118
+ WHERE a.status = 'pending' AND a.tool_key = m.old_key;
119
+
120
+ -- capybara:allow-destructive rewrites rc tool keys of pending approvals in place (resumed by run and call id, never by key)
121
+ UPDATE approvals a
122
+ SET tool_key = CASE
123
+ WHEN a.connector_id IS NOT NULL OR (
124
+ EXISTS (SELECT 1 FROM connectors c
125
+ WHERE c.org_id = a.org_id AND c.deleted_at IS NULL
126
+ AND c.slug = split_part(a.tool_key, '__', 1))
127
+ AND split_part(a.tool_key, '__', 1) NOT IN (
128
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
129
+ ) THEN 'mcp__' || a.tool_key
130
+ ELSE 'plugin__' || a.tool_key
131
+ END
132
+ WHERE a.status = 'pending'
133
+ AND a.tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND a.tool_key !~ '^(mcp|plugin)__'
134
+ AND (
135
+ a.connector_id IS NOT NULL
136
+ OR (
137
+ EXISTS (SELECT 1 FROM connectors c
138
+ WHERE c.org_id = a.org_id AND c.deleted_at IS NULL
139
+ AND c.slug = split_part(a.tool_key, '__', 1))
140
+ ) <> (
141
+ split_part(a.tool_key, '__', 1) IN (
142
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
143
+ )
144
+ );
145
+
146
+ -- capybara:allow-destructive rewrites rc tool keys of pending questions in place (resumed by run and call id, never by key)
147
+ UPDATE questions q
148
+ SET spec = jsonb_set(q.spec, '{toolKey}', to_jsonb(m.new_key))
149
+ FROM (VALUES
150
+ ('sandbox_bash', 'Bash'), ('sandbox_read', 'Read'), ('sandbox_write', 'Write'),
151
+ ('sandbox_edit', 'Edit'), ('sandbox_glob', 'Glob'), ('sandbox_grep', 'Grep'),
152
+ ('sandbox_deliver', 'Deliver'), ('skill_load', 'Skill'), ('skill_write', 'SkillWrite'),
153
+ ('ask_user', 'AskUserQuestion'), ('read_tool_output', 'ReadToolOutput'),
154
+ ('tool_search', 'ToolSearch'), ('agent', 'Agent')
155
+ ) AS m(old_key, new_key)
156
+ WHERE q.status = 'pending' AND q.spec->>'toolKey' = m.old_key;
157
+
158
+ -- capybara:allow-destructive rewrites rc tool keys of pending questions in place (resumed by run and call id, never by key)
159
+ UPDATE questions q
160
+ SET spec = jsonb_set(
161
+ q.spec,
162
+ '{toolKey}',
163
+ to_jsonb(CASE
164
+ WHEN EXISTS (SELECT 1 FROM connectors c
165
+ WHERE c.org_id = q.org_id AND c.deleted_at IS NULL
166
+ AND c.slug = split_part(q.spec->>'toolKey', '__', 1))
167
+ THEN 'mcp__' || (q.spec->>'toolKey')
168
+ ELSE 'plugin__' || (q.spec->>'toolKey')
169
+ END)
170
+ )
171
+ WHERE q.status = 'pending'
172
+ AND q.spec->>'toolKey' ~ '^[a-z0-9][a-z0-9_-]*__.' AND q.spec->>'toolKey' !~ '^(mcp|plugin)__'
173
+ AND (
174
+ EXISTS (SELECT 1 FROM connectors c
175
+ WHERE c.org_id = q.org_id AND c.deleted_at IS NULL
176
+ AND c.slug = split_part(q.spec->>'toolKey', '__', 1))
177
+ ) <> (
178
+ split_part(q.spec->>'toolKey', '__', 1) IN (
179
+ SELECT name FROM extension_installs UNION SELECT name FROM extension_name_owners)
180
+ );
181
+
182
+ -- What the startup notice counts (rows the statements above could not map), each over a partial
183
+ -- index holding only such rows, so the count at every start reads nearly empty indexes. The
184
+ -- predicate is the one of countLegacyToolKeys (packages/kernel/src/tools/legacy-data.ts).
185
+ CREATE INDEX IF NOT EXISTS tool_grants_legacy_key_idx ON tool_grants (org_id)
186
+ WHERE (tool_key = ANY (ARRAY['sandbox_bash', 'sandbox_read', 'sandbox_write', 'sandbox_edit',
187
+ 'sandbox_glob', 'sandbox_grep', 'sandbox_deliver', 'skill_load', 'skill_write', 'ask_user',
188
+ 'read_tool_output', 'tool_search', 'agent']::text[])
189
+ OR (tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND tool_key !~ '^(mcp|plugin)__'));
190
+ CREATE INDEX IF NOT EXISTS conversation_tool_grants_legacy_key_idx
191
+ ON conversation_tool_grants (conversation_id)
192
+ WHERE (tool_key = ANY (ARRAY['sandbox_bash', 'sandbox_read', 'sandbox_write', 'sandbox_edit',
193
+ 'sandbox_glob', 'sandbox_grep', 'sandbox_deliver', 'skill_load', 'skill_write', 'ask_user',
194
+ 'read_tool_output', 'tool_search', 'agent']::text[])
195
+ OR (tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND tool_key !~ '^(mcp|plugin)__'));
196
+ CREATE INDEX IF NOT EXISTS approvals_legacy_key_idx ON approvals (org_id)
197
+ WHERE status = 'pending'
198
+ AND (tool_key = ANY (ARRAY['sandbox_bash', 'sandbox_read', 'sandbox_write', 'sandbox_edit',
199
+ 'sandbox_glob', 'sandbox_grep', 'sandbox_deliver', 'skill_load', 'skill_write', 'ask_user',
200
+ 'read_tool_output', 'tool_search', 'agent']::text[])
201
+ OR (tool_key ~ '^[a-z0-9][a-z0-9_-]*__.' AND tool_key !~ '^(mcp|plugin)__'));
202
+ CREATE INDEX IF NOT EXISTS questions_legacy_key_idx ON questions (org_id)
203
+ WHERE status = 'pending'
204
+ AND ((spec->>'toolKey') = ANY (ARRAY['sandbox_bash', 'sandbox_read', 'sandbox_write',
205
+ 'sandbox_edit', 'sandbox_glob', 'sandbox_grep', 'sandbox_deliver', 'skill_load', 'skill_write',
206
+ 'ask_user', 'read_tool_output', 'tool_search', 'agent']::text[])
207
+ OR ((spec->>'toolKey') ~ '^[a-z0-9][a-z0-9_-]*__.' AND (spec->>'toolKey') !~ '^(mcp|plugin)__'));
208
+
209
+ -- Read before Write / Edit (spec §4.5): EventLog.latestFileState reads the latest tool_result of
210
+ -- a conversation whose view names a file, on every Write and Edit. Without this index it scans
211
+ -- the conversation's events backwards through the primary key, filtering each payload (EXPLAIN
212
+ -- on a 20 000-event conversation: every row removed by the filter for a path never read); with
213
+ -- it, one index probe. Partial: only tool_result rows. Built without CONCURRENTLY (a migration
214
+ -- runs in one transaction), which locks conversation_events against writes while it builds: this
215
+ -- assumes release-candidate-sized event tables, as the event indexes of 0025 and 0040 did. A
216
+ -- deployment with a large table builds it CONCURRENTLY by hand before upgrading; IF NOT EXISTS
217
+ -- then skips it here.
218
+ CREATE INDEX IF NOT EXISTS conversation_events_file_state_idx
219
+ ON conversation_events (conversation_id, ((payload->'view'->'file'->>'path')), seq DESC)
220
+ WHERE type = 'tool_result';
@@ -1,7 +0,0 @@
1
- /**
2
- * 沙箱内置工具的名字(M5 T8,Ruling M12:工具 key 只能含字母数字、`_`、`-`,所以是 `sandbox_<动作>`)。
3
- * 单独成文件:注册表与策略判定只需要名字,不引入工具实现。
4
- */
5
- export declare const SANDBOX_TOOL_NAMES: readonly ["sandbox_bash", "sandbox_read", "sandbox_write", "sandbox_edit", "sandbox_glob", "sandbox_grep", "sandbox_deliver"];
6
- /** 组织设置 `sandboxToolPolicy` 为 `ask` 时需要审批的沙箱工具(只读的 read / glob / grep 除外)。 */
7
- export declare const SANDBOX_ASK_TOOL_NAMES: ReadonlySet<string>;
@@ -1,20 +0,0 @@
1
- /**
2
- * 沙箱内置工具的名字(M5 T8,Ruling M12:工具 key 只能含字母数字、`_`、`-`,所以是 `sandbox_<动作>`)。
3
- * 单独成文件:注册表与策略判定只需要名字,不引入工具实现。
4
- */
5
- export const SANDBOX_TOOL_NAMES = [
6
- "sandbox_bash",
7
- "sandbox_read",
8
- "sandbox_write",
9
- "sandbox_edit",
10
- "sandbox_glob",
11
- "sandbox_grep",
12
- "sandbox_deliver",
13
- ];
14
- /** 组织设置 `sandboxToolPolicy` 为 `ask` 时需要审批的沙箱工具(只读的 read / glob / grep 除外)。 */
15
- export const SANDBOX_ASK_TOOL_NAMES = new Set([
16
- "sandbox_bash",
17
- "sandbox_write",
18
- "sandbox_edit",
19
- "sandbox_deliver",
20
- ]);