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

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 (312) hide show
  1. package/dist/admin/admin.controller.d.ts +9 -4
  2. package/dist/admin/admin.controller.js +13 -4
  3. package/dist/admin/extensions.controller.d.ts +8 -1
  4. package/dist/admin/extensions.controller.js +22 -2
  5. package/dist/agents/agent-lookup.d.ts +3 -0
  6. package/dist/agents/agent-lookup.js +20 -2
  7. package/dist/agents/agent-profile.service.d.ts +29 -0
  8. package/dist/agents/agent-profile.service.js +117 -0
  9. package/dist/agents/agents.controller.d.ts +93 -3
  10. package/dist/agents/agents.controller.js +114 -4
  11. package/dist/agents/agents.module.js +6 -2
  12. package/dist/agents/agents.service.d.ts +13 -5
  13. package/dist/agents/agents.service.js +49 -13
  14. package/dist/agents/definition.d.ts +35 -8
  15. package/dist/agents/definition.js +203 -17
  16. package/dist/agents/instruction-files.d.ts +60 -0
  17. package/dist/agents/instruction-files.js +111 -0
  18. package/dist/agents/models.controller.d.ts +9 -3
  19. package/dist/agents/models.controller.js +19 -4
  20. package/dist/agents/profile.d.ts +96 -0
  21. package/dist/agents/profile.js +51 -0
  22. package/dist/agents/readiness.controller.d.ts +26 -0
  23. package/dist/agents/readiness.controller.js +56 -0
  24. package/dist/agents/templates.js +20 -5
  25. package/dist/background/background.controller.d.ts +38 -0
  26. package/dist/background/background.controller.js +87 -0
  27. package/dist/background/background.module.d.ts +10 -0
  28. package/dist/background/background.module.js +32 -0
  29. package/dist/background/finish.d.ts +43 -0
  30. package/dist/background/finish.js +94 -0
  31. package/dist/background/launch-lock.d.ts +14 -0
  32. package/dist/background/launch-lock.js +39 -0
  33. package/dist/background/launch.d.ts +36 -0
  34. package/dist/background/launch.js +70 -0
  35. package/dist/background/poll.job.d.ts +53 -0
  36. package/dist/background/poll.job.js +182 -0
  37. package/dist/background/render.d.ts +23 -0
  38. package/dist/background/render.js +48 -0
  39. package/dist/background/session.d.ts +37 -0
  40. package/dist/background/session.js +66 -0
  41. package/dist/background/status.d.ts +35 -0
  42. package/dist/background/status.js +57 -0
  43. package/dist/background/stop.d.ts +62 -0
  44. package/dist/background/stop.js +225 -0
  45. package/dist/background/store.d.ts +150 -0
  46. package/dist/background/store.js +351 -0
  47. package/dist/background/store.module.d.ts +6 -0
  48. package/dist/background/store.module.js +22 -0
  49. package/dist/background/tasks.service.d.ts +67 -0
  50. package/dist/background/tasks.service.js +186 -0
  51. package/dist/background/wake-rules.d.ts +57 -0
  52. package/dist/background/wake-rules.js +83 -0
  53. package/dist/background/wake.d.ts +28 -0
  54. package/dist/background/wake.js +57 -0
  55. package/dist/catalog/catalog-content.js +4 -1
  56. package/dist/catalog/claude/agents.js +15 -10
  57. package/dist/catalog/marketplaces.service.d.ts +1 -1
  58. package/dist/catalog/plugin-import.js +1 -1
  59. package/dist/catalog/plugin-upgrade.js +1 -1
  60. package/dist/channels/cards.d.ts +8 -2
  61. package/dist/channels/cards.js +17 -9
  62. package/dist/channels/connections.service.d.ts +16 -1
  63. package/dist/channels/connections.service.js +38 -4
  64. package/dist/channels/threads.d.ts +5 -0
  65. package/dist/channels/threads.js +8 -0
  66. package/dist/config/schema.d.ts +18 -0
  67. package/dist/config/schema.js +104 -2
  68. package/dist/conversations/access.js +2 -0
  69. package/dist/conversations/artifacts.d.ts +1 -1
  70. package/dist/conversations/artifacts.js +1 -1
  71. package/dist/conversations/attachments.service.d.ts +30 -0
  72. package/dist/conversations/attachments.service.js +130 -5
  73. package/dist/conversations/conversations.controller.d.ts +36 -5
  74. package/dist/conversations/conversations.controller.js +62 -12
  75. package/dist/conversations/conversations.module.js +3 -0
  76. package/dist/conversations/conversations.service.d.ts +44 -2
  77. package/dist/conversations/conversations.service.js +140 -12
  78. package/dist/conversations/event-log.d.ts +12 -1
  79. package/dist/conversations/event-log.js +29 -1
  80. package/dist/conversations/events.d.ts +81 -3
  81. package/dist/conversations/events.js +83 -16
  82. package/dist/conversations/queue.d.ts +3 -1
  83. package/dist/conversations/queue.js +13 -2
  84. package/dist/conversations/queue.service.js +3 -1
  85. package/dist/conversations/titles.d.ts +7 -0
  86. package/dist/conversations/titles.js +15 -0
  87. package/dist/db/kernel-schema.d.ts +60 -4
  88. package/dist/db/pool.d.ts +5 -0
  89. package/dist/db/pool.js +3 -0
  90. package/dist/errors.d.ts +8 -0
  91. package/dist/errors.js +8 -0
  92. package/dist/evals/recorded-tools.d.ts +27 -1
  93. package/dist/evals/recorded-tools.js +85 -10
  94. package/dist/evals/run-replay.d.ts +10 -2
  95. package/dist/evals/run-replay.js +49 -12
  96. package/dist/evals/step-replay.js +12 -6
  97. package/dist/evals/trajectory.d.ts +2 -2
  98. package/dist/evals/trajectory.js +1 -1
  99. package/dist/extensions/admin-ops.js +2 -0
  100. package/dist/extensions/admin-view.d.ts +3 -1
  101. package/dist/extensions/admin-view.js +10 -2
  102. package/dist/extensions/blobs.js +1 -0
  103. package/dist/extensions/config-issues.d.ts +13 -1
  104. package/dist/extensions/config-issues.js +22 -5
  105. package/dist/extensions/config-json-schema.d.ts +43 -7
  106. package/dist/extensions/config-json-schema.js +124 -58
  107. package/dist/extensions/deployment-config-store.d.ts +6 -0
  108. package/dist/extensions/deployment-config.d.ts +50 -3
  109. package/dist/extensions/deployment-config.js +125 -28
  110. package/dist/extensions/display-name.d.ts +22 -0
  111. package/dist/extensions/display-name.js +77 -0
  112. package/dist/extensions/extensions.module.js +6 -0
  113. package/dist/extensions/host.d.ts +26 -1
  114. package/dist/extensions/host.js +82 -9
  115. package/dist/extensions/org-config.controller.d.ts +11 -3
  116. package/dist/extensions/org-config.controller.js +14 -6
  117. package/dist/extensions/org-config.d.ts +6 -0
  118. package/dist/extensions/org-config.js +12 -13
  119. package/dist/extensions/package.d.ts +14 -1
  120. package/dist/extensions/package.js +51 -2
  121. package/dist/extensions/permission-view.d.ts +3 -0
  122. package/dist/extensions/point-defaults.controller.d.ts +34 -0
  123. package/dist/extensions/point-defaults.controller.js +71 -0
  124. package/dist/extensions/point-defaults.d.ts +64 -0
  125. package/dist/extensions/point-defaults.js +206 -0
  126. package/dist/extensions/registry.d.ts +1 -1
  127. package/dist/extensions/registry.js +2 -0
  128. package/dist/http/api.module.js +8 -0
  129. package/dist/http/nest-logger.d.ts +6 -5
  130. package/dist/http/nest-logger.js +24 -10
  131. package/dist/identity/auth.controller.js +9 -5
  132. package/dist/identity/cookies.d.ts +20 -5
  133. package/dist/identity/cookies.js +44 -6
  134. package/dist/identity/me.controller.d.ts +5 -1
  135. package/dist/identity/me.controller.js +12 -3
  136. package/dist/identity/sessions.d.ts +12 -0
  137. package/dist/identity/sessions.js +45 -0
  138. package/dist/identity/settings.js +1 -1
  139. package/dist/index.d.ts +1 -1
  140. package/dist/infra/config/kernel-config.service.js +6 -0
  141. package/dist/infra/database/database.module.js +1 -0
  142. package/dist/infra/events/kernel-events.d.ts +8 -0
  143. package/dist/infra/lifecycle/process-guards.js +4 -0
  144. package/dist/infra/logging/logging.module.d.ts +4 -2
  145. package/dist/infra/logging/logging.module.js +24 -6
  146. package/dist/infra/queue/redis-connection.d.ts +6 -0
  147. package/dist/infra/queue/redis-connection.js +7 -0
  148. package/dist/infra/redis/redis.module.js +6 -2
  149. package/dist/infra/tokens.d.ts +6 -1
  150. package/dist/infra/tokens.js +6 -1
  151. package/dist/lifecycle/kernel-lifecycle.js +5 -0
  152. package/dist/log.d.ts +13 -1
  153. package/dist/log.js +93 -17
  154. package/dist/models/purposes.d.ts +23 -0
  155. package/dist/models/purposes.js +31 -10
  156. package/dist/orgs/offboarding.d.ts +3 -1
  157. package/dist/orgs/offboarding.js +26 -16
  158. package/dist/orgs/settings.service.d.ts +7 -2
  159. package/dist/orgs/settings.service.js +15 -4
  160. package/dist/roles/worker.module.js +6 -0
  161. package/dist/runtime/context/build.d.ts +15 -13
  162. package/dist/runtime/context/build.js +45 -27
  163. package/dist/runtime/context/claude-tools.d.ts +3 -1
  164. package/dist/runtime/context/claude-tools.js +12 -12
  165. package/dist/runtime/context/compaction.js +4 -0
  166. package/dist/runtime/context/legacy-text.d.ts +22 -0
  167. package/dist/runtime/context/legacy-text.js +37 -0
  168. package/dist/runtime/context/skills.d.ts +29 -11
  169. package/dist/runtime/context/skills.js +37 -30
  170. package/dist/runtime/langgraph-loop.js +3 -2
  171. package/dist/runtime/loop.d.ts +5 -4
  172. package/dist/runtime/maintenance.d.ts +4 -2
  173. package/dist/runtime/maintenance.js +17 -5
  174. package/dist/runtime/model-call.d.ts +17 -1
  175. package/dist/runtime/model-call.js +4 -3
  176. package/dist/runtime/providers.d.ts +6 -0
  177. package/dist/runtime/providers.js +8 -0
  178. package/dist/runtime/run-finish.d.ts +1 -1
  179. package/dist/runtime/run-queue.d.ts +28 -5
  180. package/dist/runtime/run-queue.js +131 -35
  181. package/dist/runtime/run-worker.d.ts +2 -2
  182. package/dist/runtime/run-worker.js +20 -7
  183. package/dist/runtime/runtime.module.js +8 -3
  184. package/dist/runtime/step-log.d.ts +6 -0
  185. package/dist/runtime/step-log.js +16 -2
  186. package/dist/runtime/subagents/definition.d.ts +1 -6
  187. package/dist/runtime/subagents/definition.js +14 -13
  188. package/dist/runtime/subagents/dispatch.d.ts +2 -2
  189. package/dist/runtime/subagents/dispatch.js +2 -2
  190. package/dist/runtime/subagents/org-agents.js +2 -1
  191. package/dist/runtime/subagents/roster.d.ts +1 -1
  192. package/dist/runtime/subagents/roster.js +1 -1
  193. package/dist/runtime/subagents/skill-fork.d.ts +2 -2
  194. package/dist/runtime/subagents/skill-fork.js +2 -2
  195. package/dist/runtime/subagents/subagents.service.d.ts +3 -3
  196. package/dist/runtime/subagents/subagents.service.js +3 -3
  197. package/dist/runtime/usage-report.d.ts +2 -1
  198. package/dist/runtime/usage-report.js +2 -1
  199. package/dist/runtime/waits.d.ts +13 -7
  200. package/dist/runtime/waits.js +42 -16
  201. package/dist/sandbox/sandbox.module.js +6 -1
  202. package/dist/sandbox/sandbox.service.d.ts +61 -3
  203. package/dist/sandbox/sandbox.service.js +141 -7
  204. package/dist/skills/catalog.d.ts +4 -4
  205. package/dist/skills/catalog.js +4 -4
  206. package/dist/skills/changes.service.d.ts +2 -2
  207. package/dist/skills/changes.service.js +5 -4
  208. package/dist/skills/invocation-keys.d.ts +1 -1
  209. package/dist/skills/invocation-keys.js +1 -1
  210. package/dist/skills/invoke.d.ts +1 -1
  211. package/dist/skills/invoke.js +5 -4
  212. package/dist/tools/approvals.service.d.ts +6 -0
  213. package/dist/tools/approvals.service.js +21 -1
  214. package/dist/tools/builtin-names.d.ts +91 -0
  215. package/dist/tools/builtin-names.js +143 -0
  216. package/dist/tools/builtins/agent.d.ts +7 -7
  217. package/dist/tools/builtins/agent.js +7 -11
  218. package/dist/tools/builtins/ask-user.d.ts +1 -1
  219. package/dist/tools/builtins/ask-user.js +2 -2
  220. package/dist/tools/builtins/builtin-tools.d.ts +17 -3
  221. package/dist/tools/builtins/builtin-tools.js +36 -4
  222. package/dist/tools/builtins/read-tool-output.d.ts +1 -1
  223. package/dist/tools/builtins/read-tool-output.js +2 -2
  224. package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
  225. package/dist/tools/builtins/sandbox/bash.js +259 -29
  226. package/dist/tools/builtins/sandbox/common.d.ts +30 -7
  227. package/dist/tools/builtins/sandbox/common.js +9 -5
  228. package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
  229. package/dist/tools/builtins/sandbox/deliver.js +12 -12
  230. package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
  231. package/dist/tools/builtins/sandbox/edit.js +41 -30
  232. package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
  233. package/dist/tools/builtins/sandbox/file-state.js +34 -0
  234. package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
  235. package/dist/tools/builtins/sandbox/glob-match.js +1 -1
  236. package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
  237. package/dist/tools/builtins/sandbox/glob.js +4 -4
  238. package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
  239. package/dist/tools/builtins/sandbox/grep.js +147 -30
  240. package/dist/tools/builtins/sandbox/index.d.ts +3 -1
  241. package/dist/tools/builtins/sandbox/index.js +7 -1
  242. package/dist/tools/builtins/sandbox/read.d.ts +2 -2
  243. package/dist/tools/builtins/sandbox/read.js +21 -14
  244. package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
  245. package/dist/tools/builtins/sandbox/task-output.js +304 -0
  246. package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
  247. package/dist/tools/builtins/sandbox/task-stop.js +112 -0
  248. package/dist/tools/builtins/sandbox/write.d.ts +1 -1
  249. package/dist/tools/builtins/sandbox/write.js +37 -33
  250. package/dist/tools/builtins/skill-load.d.ts +3 -3
  251. package/dist/tools/builtins/skill-load.js +32 -16
  252. package/dist/tools/builtins/skill-write.d.ts +4 -4
  253. package/dist/tools/builtins/skill-write.js +10 -10
  254. package/dist/tools/builtins/tool-search.d.ts +13 -9
  255. package/dist/tools/builtins/tool-search.js +25 -14
  256. package/dist/tools/builtins/web/errors.d.ts +8 -0
  257. package/dist/tools/builtins/web/errors.js +53 -0
  258. package/dist/tools/builtins/web/extract.d.ts +62 -0
  259. package/dist/tools/builtins/web/extract.js +267 -0
  260. package/dist/tools/builtins/web/fetch.d.ts +55 -0
  261. package/dist/tools/builtins/web/fetch.js +174 -0
  262. package/dist/tools/builtins/web/index.d.ts +13 -0
  263. package/dist/tools/builtins/web/index.js +12 -0
  264. package/dist/tools/builtins/web/page-cache.d.ts +25 -0
  265. package/dist/tools/builtins/web/page-cache.js +77 -0
  266. package/dist/tools/builtins/web/search.d.ts +25 -0
  267. package/dist/tools/builtins/web/search.js +209 -0
  268. package/dist/tools/builtins/web/url.d.ts +17 -0
  269. package/dist/tools/builtins/web/url.js +43 -0
  270. package/dist/tools/connectors.service.js +4 -4
  271. package/dist/tools/display-names.d.ts +9 -0
  272. package/dist/tools/display-names.js +24 -0
  273. package/dist/tools/drift.js +5 -5
  274. package/dist/tools/elicitation.js +1 -1
  275. package/dist/tools/info.d.ts +20 -4
  276. package/dist/tools/info.js +55 -32
  277. package/dist/tools/legacy-data.d.ts +34 -0
  278. package/dist/tools/legacy-data.js +76 -0
  279. package/dist/tools/legacy-keys.d.ts +51 -0
  280. package/dist/tools/legacy-keys.js +115 -0
  281. package/dist/tools/naming.d.ts +1 -5
  282. package/dist/tools/naming.js +1 -7
  283. package/dist/tools/oauth/flow-paths.d.ts +1 -1
  284. package/dist/tools/oauth/flow-paths.js +1 -1
  285. package/dist/tools/outputs.js +1 -1
  286. package/dist/tools/pipeline.d.ts +13 -7
  287. package/dist/tools/pipeline.js +112 -39
  288. package/dist/tools/policy.d.ts +1 -14
  289. package/dist/tools/policy.js +13 -19
  290. package/dist/tools/questions.controller.d.ts +1 -1
  291. package/dist/tools/questions.controller.js +1 -1
  292. package/dist/tools/questions.service.d.ts +1 -1
  293. package/dist/tools/registry.d.ts +38 -24
  294. package/dist/tools/registry.js +54 -47
  295. package/dist/tools/resources.d.ts +1 -1
  296. package/dist/tools/resources.js +1 -1
  297. package/dist/tools/tools.module.js +13 -1
  298. package/dist/tools/validate.js +1 -1
  299. package/package.json +3 -3
  300. package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
  301. package/src/migrations/0064_account_last_org.sql +7 -0
  302. package/src/migrations/0065_conversation_archive.sql +8 -0
  303. package/src/migrations/0066_extension_display_name.sql +4 -0
  304. package/src/migrations/0067_agent_profiles.sql +20 -0
  305. package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
  306. package/src/migrations/0069_attachments_agent_idx.sql +3 -0
  307. package/src/migrations/0070_background_tasks.sql +37 -0
  308. package/src/migrations/0071_background_tasks_reserve.sql +6 -0
  309. package/src/migrations/0072_background_tasks_stopping.sql +5 -0
  310. package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
  311. package/dist/tools/builtins/sandbox/names.d.ts +0 -7
  312. package/dist/tools/builtins/sandbox/names.js +0 -20
@@ -11,6 +11,7 @@ import { randomUUID } from "node:crypto";
11
11
  import { isSandboxGoneError, } from "@capybara-hq/plugin-sdk";
12
12
  import { Inject, Injectable } from "@nestjs/common";
13
13
  import { sql, } from "kysely";
14
+ import { BackgroundTaskStore } from "../background/store.js";
14
15
  import { DEFAULT_MAX_COMMAND_SECONDS, DEFAULT_MAX_RUN_SECONDS, } from "../config/schema.js";
15
16
  import { ApiError } from "../errors.js";
16
17
  import { readSandboxLimitOverride } from "../identity/settings.js";
@@ -36,6 +37,10 @@ const SWEEP_BATCH = 200;
36
37
  const SWEEP_CONCURRENCY = 4;
37
38
  /** destroyFor 等锁的上限:持有者在跑命令时最多等这么久。 */
38
39
  const DESTROY_FOR_WAIT_MS = 10 * 60_000;
40
+ /** The time limit of the exec that kills lost background tasks after a resume (ms). */
41
+ const PENDING_KILL_TIMEOUT_MS = 30_000;
42
+ /** A background task's session id as `echo $!` printed it; anything else is never put in a command. */
43
+ const SID_RE = /^[1-9][0-9]{0,9}$/;
39
44
  /** 维护里一次提供方操作的 signal:超时或锁丢失时中止。 */
40
45
  const opSignal = (lease) => AbortSignal.any([lease.signal, AbortSignal.timeout(SWEEP_OP_TIMEOUT_MS)]);
41
46
  /**
@@ -180,17 +185,21 @@ let SandboxService = class SandboxService {
180
185
  providers;
181
186
  config;
182
187
  log;
188
+ background;
183
189
  constructor(db,
184
190
  /** The record of the instance each holder last used (`capk:sandbox-seen:…`). */
185
191
  redis, leases,
186
192
  /** The `sandbox@1` provider (read at every call: providers are ready after the extension load). */
187
- providers, config, log) {
193
+ providers, config, log,
194
+ /** Background tasks lost while their process may live on (killed after a resume). */
195
+ background) {
188
196
  this.db = db;
189
197
  this.redis = redis;
190
198
  this.leases = leases;
191
199
  this.providers = providers;
192
200
  this.config = config;
193
201
  this.log = log;
202
+ this.background = background;
194
203
  }
195
204
  /** Poll interval while waiting for a lease (the leases' own). */
196
205
  get pollMs() {
@@ -219,6 +228,82 @@ let SandboxService = class SandboxService {
219
228
  return null;
220
229
  return { status: row.status, lastUsedAt: row.last_used_at };
221
230
  }
231
+ /**
232
+ * The instance the conversation's sandbox records now (its `created_at`), while it has one that
233
+ * runs or is suspended; null otherwise. Read without a lease: a caller inside `withSandbox`
234
+ * reads the instance its own lease keeps.
235
+ */
236
+ async currentInstance(conversationId) {
237
+ const row = await this.db
238
+ .selectFrom("sandboxes")
239
+ .select(["status", "handle", "created_at"])
240
+ .where("conversation_id", "=", conversationId)
241
+ .executeTakeFirst();
242
+ if (!row || row.handle === null)
243
+ return null;
244
+ if (row.status !== "running" && row.status !== "suspended")
245
+ return null;
246
+ return { createdAt: row.created_at };
247
+ }
248
+ /**
249
+ * Runs `fn` on the conversation's sandbox only while it runs the instance created at
250
+ * `instance.createdAt` (background tasks, spec 2026-11-05-background-bash-design §4.3). It never
251
+ * creates, resumes or rebuilds a sandbox: a sandbox that is missing, suspended, being created or
252
+ * destroyed (or recorded with another provider) is `not_running`, one that records another
253
+ * instance is `other_instance`. Otherwise `fn` runs under a shared lease, as a `withSandbox`
254
+ * holder does (its tag on every exec, crashed holders' commands killed), and `last_used_at`
255
+ * moves forward when it ends. Waits for the lease up to `sandbox.lockWaitSeconds`
256
+ * (`sandbox.busy`); `signal` (optional) ends the wait with its abort reason. The signal passed
257
+ * to `fn` aborts with `signal` or when the lease is lost. Errors of `fn` (`SandboxGoneError`
258
+ * too) are thrown as they are.
259
+ */
260
+ async ifRunning(conversationId, instance, fn, signal = new AbortController().signal) {
261
+ const p = this.require();
262
+ const conv = conversationId;
263
+ const cap = this.effectiveCap(p);
264
+ const deadline = Date.now() + this.config.get().sandbox.lockWaitSeconds * 1000;
265
+ const token = randomUUID();
266
+ for (;;) {
267
+ if (signal.aborted)
268
+ throw abortReason(signal);
269
+ const row = await this.db
270
+ .selectFrom("sandboxes")
271
+ .selectAll()
272
+ .where("conversation_id", "=", conv)
273
+ .executeTakeFirst();
274
+ if (!row)
275
+ return { kind: "not_running", status: "gone" };
276
+ if (row.created_at.getTime() !== instance.createdAt.getTime()) {
277
+ return { kind: "other_instance" };
278
+ }
279
+ if (!this.usable(row, p, null))
280
+ return { kind: "not_running", status: row.status };
281
+ // the instance this check saw: its handle plus `created_at` (never `generation`, which
282
+ // every lease bumps)
283
+ const want = { handle: row.handle, createdAt: row.created_at.getTime() };
284
+ if (!(await this.leases.legacyMarker(conv))) {
285
+ const lease = await this.leases.tryShared(conv, cap, token);
286
+ if (lease) {
287
+ const e = await this.asShared({ orgId: row.org_id, conversationId: conv }, p, lease, (r) => this.usable(r, p, null) && isInstance(r, want), signal);
288
+ if (e) {
289
+ try {
290
+ const impl = tagged(p.impl, lease.token, () => this.register(conv, lease.token));
291
+ const sig = AbortSignal.any([signal, lease.signal]);
292
+ return { kind: "ran", value: await fn(impl, e.handle, sig) };
293
+ }
294
+ finally {
295
+ await this.leave(conv, e);
296
+ }
297
+ }
298
+ // the row changed between the check and the lease: classify it again
299
+ continue;
300
+ }
301
+ }
302
+ if (Date.now() + this.pollMs > deadline)
303
+ throw sandboxBusy();
304
+ await sleep(this.pollMs + Math.floor(Math.random() * this.pollMs), signal);
305
+ }
306
+ }
222
307
  /**
223
308
  * A short lease on one path of the conversation's sandbox around the kernel's own
224
309
  * read-modify-write of that file (spec §10.4); waits up to 30 s, then throws
@@ -318,7 +403,7 @@ let SandboxService = class SandboxService {
318
403
  if (intent)
319
404
  await this.clearIntent(conv, token);
320
405
  intent = false;
321
- const e = await this.asShared(ctx, p, lease, gone, signal);
406
+ const e = await this.asShared(ctx, p, lease, (r) => this.usable(r, p, gone), signal);
322
407
  if (e)
323
408
  return e;
324
409
  }
@@ -365,14 +450,14 @@ let SandboxService = class SandboxService {
365
450
  !isInstance(row, gone));
366
451
  }
367
452
  /**
368
- * Under a just-taken shared lease: confirms the row is still usable (else releases and returns
369
- * null), records the holder and kills crashed holders' commands.
453
+ * Under a just-taken shared lease: confirms the row is still `accept`ed (else releases and
454
+ * returns null), records the holder and kills crashed holders' commands.
370
455
  */
371
- async asShared(ctx, p, lease, gone, signal) {
456
+ async asShared(ctx, p, lease, accept, signal) {
372
457
  const conv = ctx.conversationId;
373
458
  try {
374
459
  const row = await this.readRow(ctx);
375
- if (!this.usable(row, p, gone)) {
460
+ if (!accept(row)) {
376
461
  await lease.release();
377
462
  return null;
378
463
  }
@@ -429,6 +514,9 @@ let SandboxService = class SandboxService {
429
514
  if (!s.info.fresh && (s.resumed || dead.legacyMarker || dead.tags.length > 0)) {
430
515
  await this.killLeftovers(conv, s.handle, p, sig, s.resumed && !dead.legacyMarker && dead.tags.length === 0 ? "resume" : "takeover");
431
516
  }
517
+ if (s.resumed || s.info.fresh) {
518
+ await this.killPendingTasks(conv, s.handle, s.createdAt, p, lease.token, sig);
519
+ }
432
520
  const shared = await lease.downgrade();
433
521
  await this.register(conv, shared.token);
434
522
  return { lease: shared, handle: s.handle, createdAt: s.createdAt, info: s.info };
@@ -618,6 +706,51 @@ let SandboxService = class SandboxService {
618
706
  }
619
707
  await this.kill(conversationId, why, () => killAll.call(p.impl, handle, signal), signal);
620
708
  }
709
+ /**
710
+ * Under the exclusive lease, right after a resume or a (re)build: kills the sessions of the
711
+ * background tasks marked lost while their process may live on (`kill_pending`), in one exec of
712
+ * `pkill -KILL -s <sid>`, then clears their flags. Tasks of another instance have no process
713
+ * here and are only cleared. A gone instance and an abort are thrown; another failure is warned
714
+ * and leaves the flags of this instance's tasks for the next resume.
715
+ */
716
+ async killPendingTasks(conversationId, handle, createdAt, p, tag, signal) {
717
+ const rows = await this.background.pendingKills(conversationId);
718
+ if (rows.length === 0)
719
+ return;
720
+ const mine = rows.filter((r) => r.sandboxCreatedAt?.getTime() === createdAt && r.sid !== null && SID_RE.test(r.sid));
721
+ let cleared = rows;
722
+ if (mine.length > 0) {
723
+ // pkill exits 1 when the session has no process left: only a higher status is a failure
724
+ const command = `rc=0; for sid in ${mine.map((r) => r.sid).join(" ")}; do pkill -KILL -s "$sid"; c=$?; [ "$c" -le 1 ] || rc=$c; done; exit $rc`;
725
+ try {
726
+ const r = await p.impl.exec(handle, {
727
+ command,
728
+ timeoutMs: PENDING_KILL_TIMEOUT_MS,
729
+ maxOutputBytes: 4096,
730
+ signal,
731
+ tag,
732
+ });
733
+ if (r.exitCode !== 0) {
734
+ throw new Error(`pkill exited with ${r.exitCode === null ? "no status" : r.exitCode}: ${r.stderr.slice(0, 500)}`);
735
+ }
736
+ this.log.info("lost background tasks killed after a sandbox resume", {
737
+ conversationId,
738
+ tasks: mine.map((t) => t.id),
739
+ });
740
+ }
741
+ catch (err) {
742
+ if (signal.aborted || isSandboxGoneError(err))
743
+ throw err;
744
+ this.log.warn("cannot kill lost background tasks after a sandbox resume", {
745
+ conversationId,
746
+ err,
747
+ });
748
+ const kept = new Set(mine.map((r) => r.id));
749
+ cleared = rows.filter((r) => !kept.has(r.id));
750
+ }
751
+ }
752
+ await this.background.clearKill(cleared.map((r) => r.id));
753
+ }
621
754
  require() {
622
755
  const p = this.providers.current();
623
756
  if (!p)
@@ -934,6 +1067,7 @@ SandboxService = __decorate([
934
1067
  __param(2, Inject(SandboxLeases)),
935
1068
  __param(3, Inject(SandboxProviders)),
936
1069
  __param(4, Inject(KernelConfigService)),
937
- __param(5, Inject(KERNEL_LOGGER))
1070
+ __param(5, Inject(KERNEL_LOGGER)),
1071
+ __param(6, Inject(BackgroundTaskStore))
938
1072
  ], SandboxService);
939
1073
  export { SandboxService };
@@ -11,7 +11,7 @@ import { type CatalogSkill, type PinnedSkill, type SkillsSnapshot } from "../run
11
11
  import { type PrefixedStorage, StorageService } from "../storage/storage.service.js";
12
12
  /**
13
13
  * 技能目录快照(M5 T14,Ruling M17):运行开始时解析「此刻对这个对话生效的技能」,与日志里最新一份快照不同才写
14
- * `skills_catalog`。模型看到的目录与 `skill_load` 的名字解析只来自日志,不读实时的库。
14
+ * `skills_catalog`。模型看到的目录与 `Skill` 的名字解析只来自日志,不读实时的库。
15
15
  */
16
16
  /**
17
17
  * Pinned skill limits (spec §9.1): at most `count` pinned skills per agent, whose current SKILL.md
@@ -103,9 +103,9 @@ export declare function loadPinnedBodies(storage: PrefixedStorage, orgId: string
103
103
  export declare function fitPinned(pinned: readonly PinnedSkill[], log: Pick<Logger, "warn"> | undefined, input: Partial<Pick<EffectiveSkillsInput, "orgId" | "agentId" | "conversationId">>): PinnedSkill[];
104
104
  /** `skills_catalog` 的幂等键:每次运行最多写一份。 */
105
105
  /**
106
- * 是否给模型 `skill_load`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
106
+ * 是否给模型 `Skill`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
107
107
  * 用同一判定。不因空快照收回这个工具:工具集合一变缓存前缀就失效;而且 agent 没有别的工具时会发出「没有工具
108
- * 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 skill_load 由 `NO_SKILLS_TEXT` 给出可执行的说明。
108
+ * 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 Skill 由 `NO_SKILLS_TEXT` 给出可执行的说明。
109
109
  */
110
110
  export declare function skillLoadOffered(latest: Extract<ConversationEvent, {
111
111
  type: "skills_catalog";
@@ -124,7 +124,7 @@ export declare const skillsCatalogIdemKey: (runId: string) => string;
124
124
  /**
125
125
  * 在调用方事务里锁会话行(与 `EventLog.append` 同一把锁)并读最新一份技能目录快照(没有为 undefined)。
126
126
  * `db` is the caller's transaction handle (`KernelDb.db` inside `@Transactional`).
127
- * 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、skill_write、拒绝时的回退)插不进来。
127
+ * 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、SkillWrite、拒绝时的回退)插不进来。
128
128
  */
129
129
  export declare function lockLatestCatalog(db: KernelSchemaDb, conversationId: string): Promise<SkillsSnapshot | undefined>;
130
130
  /**
@@ -23,7 +23,7 @@ import { sameSnapshot, } from "../runtime/context/skills.js";
23
23
  import { StorageService } from "../storage/storage.service.js";
24
24
  /**
25
25
  * 技能目录快照(M5 T14,Ruling M17):运行开始时解析「此刻对这个对话生效的技能」,与日志里最新一份快照不同才写
26
- * `skills_catalog`。模型看到的目录与 `skill_load` 的名字解析只来自日志,不读实时的库。
26
+ * `skills_catalog`。模型看到的目录与 `Skill` 的名字解析只来自日志,不读实时的库。
27
27
  */
28
28
  /**
29
29
  * Pinned skill limits (spec §9.1): at most `count` pinned skills per agent, whose current SKILL.md
@@ -303,9 +303,9 @@ export function fitPinned(pinned, log, input) {
303
303
  }
304
304
  /** `skills_catalog` 的幂等键:每次运行最多写一份。 */
305
305
  /**
306
- * 是否给模型 `skill_load`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
306
+ * 是否给模型 `Skill`:日志里有过技能目录快照(Ruling M17),**即使最新一份是空的**。构建请求与执行工具调用
307
307
  * 用同一判定。不因空快照收回这个工具:工具集合一变缓存前缀就失效;而且 agent 没有别的工具时会发出「没有工具
308
- * 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 skill_load 由 `NO_SKILLS_TEXT` 给出可执行的说明。
308
+ * 但历史里有 tool_use」的请求,Anthropic 会拒绝。空目录下调用 Skill 由 `NO_SKILLS_TEXT` 给出可执行的说明。
309
309
  */
310
310
  export function skillLoadOffered(latest,
311
311
  /**
@@ -336,7 +336,7 @@ export const skillsCatalogIdemKey = (runId) => `skills_catalog:${runId}`;
336
336
  /**
337
337
  * 在调用方事务里锁会话行(与 `EventLog.append` 同一把锁)并读最新一份技能目录快照(没有为 undefined)。
338
338
  * `db` is the caller's transaction handle (`KernelDb.db` inside `@Transactional`).
339
- * 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、skill_write、拒绝时的回退)插不进来。
339
+ * 「读最新 → 推出下一份 → 追加」都在这把锁内,别的写快照的事务(运行开始、SkillWrite、拒绝时的回退)插不进来。
340
340
  */
341
341
  export async function lockLatestCatalog(db, conversationId) {
342
342
  await db
@@ -97,9 +97,9 @@ export interface CancelledDrafts {
97
97
  }
98
98
  /**
99
99
  * agent 自写技能与共享 agent 的技能变更审批(M5 T15,R4):
100
- * - `write`(工具 `skill_write` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
100
+ * - `write`(工具 `SkillWrite` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
101
101
  * 期间只有发起对话试用(`resolveEffectiveSkills` 按对话合并);两种情况都在同一事务里往发起对话追加一份
102
- * 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `skill_load`;
102
+ * 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `Skill`;
103
103
  * - `decide`(HTTP,只有该 agent 当前的负责人):批准 → 版本对所有对话生效(各对话下一次运行开始时写新快照);
104
104
  * 拒绝 → 删除草稿对象,发起对话下一次运行开始时目录回到当前版本;
105
105
  * - 同一 agent 的同名技能最多一条待处理申请:同一对话再写取代旧的(旧的 cancelled、草稿删除、技能 id 不变),
@@ -22,6 +22,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
22
22
  import { KERNEL_LOGGER } from "../infra/tokens.js";
23
23
  import { sameSnapshot, } from "../runtime/context/skills.js";
24
24
  import { StorageService } from "../storage/storage.service.js";
25
+ import { BUILTIN } from "../tools/builtin-names.js";
25
26
  import { fitPinned, invocationOf, lockLatestCatalog } from "./catalog.js";
26
27
  /**
27
28
  * 每个 agent 自己的技能(`scope = agent`)上限,含待批准的新技能。远低于目录快照的上限(`SKILLS_CATALOG_MAX`),
@@ -42,7 +43,7 @@ const AGENT_GONE_TEXT = "This agent no longer exists, so the skill cannot be sav
42
43
  const iso = (v) => new Date(v).toISOString();
43
44
  const byName = (a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
44
45
  const notFound = () => new ApiError("skill_change.not_found", "Skill change not found");
45
- /** The body of the SKILL.md a skill_write just stored (as run start would read it); null if unparsable. */
46
+ /** The body of the SKILL.md a SkillWrite just stored (as run start would read it); null if unparsable. */
46
47
  function writtenBody(pkg) {
47
48
  const md = pkg.files.find((f) => f.path === SKILL_ENTRY_FILE);
48
49
  if (!md)
@@ -57,9 +58,9 @@ function writtenBody(pkg) {
57
58
  }
58
59
  /**
59
60
  * agent 自写技能与共享 agent 的技能变更审批(M5 T15,R4):
60
- * - `write`(工具 `skill_write` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
61
+ * - `write`(工具 `SkillWrite` 调用):私有 agent 直接生成新版本;共享 agent 生成一条待负责人批准的申请,
61
62
  * 期间只有发起对话试用(`resolveEffectiveSkills` 按对话合并);两种情况都在同一事务里往发起对话追加一份
62
- * 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `skill_load`;
63
+ * 技能目录快照(Ruling M17:日志是唯一事实源),同一运行的下一步即可 `Skill`;
63
64
  * - `decide`(HTTP,只有该 agent 当前的负责人):批准 → 版本对所有对话生效(各对话下一次运行开始时写新快照);
64
65
  * 拒绝 → 删除草稿对象,发起对话下一次运行开始时目录回到当前版本;
65
66
  * - 同一 agent 的同名技能最多一条待处理申请:同一对话再写取代旧的(旧的 cancelled、草稿删除、技能 id 不变),
@@ -245,7 +246,7 @@ let SkillChangesService = class SkillChangesService {
245
246
  files: pkg.manifest.files.length,
246
247
  agentId: ctx.agentId,
247
248
  conversationId: ctx.conversationId,
248
- via: "skill_write",
249
+ via: BUILTIN.SkillWrite,
249
250
  },
250
251
  });
251
252
  outcome = { kind: "version", skillId, version, created: !existing };
@@ -2,7 +2,7 @@
2
2
  * The SKILL.md invocation keys (package ecosystem phase D) and their lenient reading: readers of
3
3
  * stored files (catalog install and upgrade) and the Claude Code translation drop a key the strict
4
4
  * `parseSkillMd` checks would refuse, with the reason, instead of refusing the skill or storing an
5
- * invocation nobody can use. Uploads, native imports and `skill_write` stay strict.
5
+ * invocation nobody can use. Uploads, native imports and `SkillWrite` stay strict.
6
6
  */
7
7
  /** The frontmatter keys that make up a skill's invocation metadata. */
8
8
  export declare const INVOCATION_KEYS: readonly ["disable-model-invocation", "user-invocable", "context", "agent", "argument-hint", "arguments"];
@@ -3,7 +3,7 @@ import { parseSkillMd } from "@capybara-hq/plugin-sdk";
3
3
  * The SKILL.md invocation keys (package ecosystem phase D) and their lenient reading: readers of
4
4
  * stored files (catalog install and upgrade) and the Claude Code translation drop a key the strict
5
5
  * `parseSkillMd` checks would refuse, with the reason, instead of refusing the skill or storing an
6
- * invocation nobody can use. Uploads, native imports and `skill_write` stay strict.
6
+ * invocation nobody can use. Uploads, native imports and `SkillWrite` stay strict.
7
7
  */
8
8
  /** The frontmatter keys that make up a skill's invocation metadata. */
9
9
  export const INVOCATION_KEYS = [
@@ -121,7 +121,7 @@ export declare class SkillInvoker {
121
121
  * `storage.unavailable` (`skill_unreadable`) when the version is gone or its SKILL.md cannot be
122
122
  * read, plain `storage.unavailable` when storage is down. `task` renders the text for the
123
123
  * sub-agent's effective definition: the shell-injection line only when the sub-agent has
124
- * `sandbox_bash` (batch 3 review m1).
124
+ * `Bash` (batch 3 review m1).
125
125
  */
126
126
  renderTask(a: {
127
127
  orgId: string;
@@ -15,6 +15,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
15
15
  import { KERNEL_LOGGER } from "../infra/tokens.js";
16
16
  import { isGeneralAlias, resolveSkillForkType } from "../runtime/subagents/roster.js";
17
17
  import { StorageService } from "../storage/storage.service.js";
18
+ import { BUILTIN } from "../tools/builtin-names.js";
18
19
  import { findSkillVersion, readSkillFile, SkillIntegrityError, } from "../tools/builtins/skill-load.js";
19
20
  import { invocationOf, resolveSkills } from "./catalog.js";
20
21
  /** A leading `/<name>` or `/<plugin>:<name>`, then whitespace or the end (spec §3.2). */
@@ -142,10 +143,10 @@ let SkillInvoker = class SkillInvoker {
142
143
  const body = await this.readBody(a.orgId, found);
143
144
  const others = found.manifest.files.some((f) => f.path !== SKILL_ENTRY_FILE);
144
145
  const lines = {
145
- // the invocation itself makes the conversation's next run offer skill_load (skillLoadOffered)
146
+ // the invocation itself makes the conversation's next run offer Skill (skillLoadOffered)
146
147
  ...(others && { files: "load" }),
147
148
  ...(hasShellInjection(body) &&
148
- hasBuiltin(a.agent, "sandbox_bash") && { shellHint: true }),
149
+ hasBuiltin(a.agent, BUILTIN.Bash) && { shellHint: true }),
149
150
  };
150
151
  const input = {
151
152
  name: a.name,
@@ -192,7 +193,7 @@ let SkillInvoker = class SkillInvoker {
192
193
  * `storage.unavailable` (`skill_unreadable`) when the version is gone or its SKILL.md cannot be
193
194
  * read, plain `storage.unavailable` when storage is down. `task` renders the text for the
194
195
  * sub-agent's effective definition: the shell-injection line only when the sub-agent has
195
- * `sandbox_bash` (batch 3 review m1).
196
+ * `Bash` (batch 3 review m1).
196
197
  */
197
198
  async renderTask(a) {
198
199
  const { skill } = a;
@@ -225,7 +226,7 @@ let SkillInvoker = class SkillInvoker {
225
226
  lines: {
226
227
  ...(others && { files: "load" }),
227
228
  ...(hasShellInjection(body) &&
228
- hasBuiltin(child, "sandbox_bash") && { shellHint: true }),
229
+ hasBuiltin(child, BUILTIN.Bash) && { shellHint: true }),
229
230
  },
230
231
  lead: false,
231
232
  });
@@ -1,3 +1,4 @@
1
+ import { type LocalizedName } from "@capybara-hq/plugin-sdk";
1
2
  import { EventLog } from "../conversations/event-log.js";
2
3
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
3
4
  import { KernelDb } from "../infra/database/kernel-db.js";
@@ -43,6 +44,11 @@ export interface ApprovalView extends ApprovalRecord {
43
44
  name: string;
44
45
  slug: string;
45
46
  } | null;
47
+ /** An extension tool's plugin (`plugin__<plugin>__<tool>`) and its display name (display only). */
48
+ extension: {
49
+ name: string;
50
+ displayName: LocalizedName;
51
+ } | null;
46
52
  agent: {
47
53
  id: string;
48
54
  name: string;
@@ -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 { randomUUID } from "node:crypto";
11
+ import { connectorToolKey, parseToolKey } from "@capybara-hq/plugin-sdk";
11
12
  import { Inject, Injectable } from "@nestjs/common";
12
13
  import { sql } from "kysely";
13
14
  import { EventLog } from "../conversations/event-log.js";
@@ -19,6 +20,7 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
19
20
  import { Transactional } from "../infra/database/transactional.decorator.js";
20
21
  import { KernelEvents } from "../infra/events/kernel-events.js";
21
22
  import { ORG_ROLES, roleAtLeast } from "../orgs/roles.js";
23
+ import { pluginDisplayNames } from "./display-names.js";
22
24
  export { APPROVAL_DENY_REASON_MAX_CHARS } from "../conversations/events.js";
23
25
  /** `list` 一次最多返回的审批数(收件箱据此判断这个来源是否取满)。 */
24
26
  export const APPROVALS_LIST_MAX = 100;
@@ -149,6 +151,8 @@ let ApprovalsService = class ApprovalsService {
149
151
  toolKey: rec.toolKey,
150
152
  expiresAt: rec.expiresAt,
151
153
  ...(input.policy !== undefined && { policy: input.policy }),
154
+ requestedFor: rec.requestedFor,
155
+ approverRole: rec.approverRole,
152
156
  },
153
157
  },
154
158
  ]);
@@ -579,12 +583,22 @@ let ApprovalsService = class ApprovalsService {
579
583
  .where("id", "in", connectorIds)
580
584
  .execute();
581
585
  const connectorById = new Map(apps.map((a) => [a.id, a]));
586
+ const pluginOf = (r) => {
587
+ if (r.connectorId !== null)
588
+ return null;
589
+ const p = parseToolKey(r.toolKey);
590
+ return p?.kind === "extension" ? p.plugin : null;
591
+ };
592
+ const displayNames = await pluginDisplayNames(db.db, recs.flatMap((r) => {
593
+ const p = pluginOf(r);
594
+ return p === null ? [] : [p];
595
+ }));
582
596
  return recs.flatMap((r) => {
583
597
  const c = ctx.get(r.runId);
584
598
  if (!c)
585
599
  return [];
586
600
  const app = r.connectorId === null ? undefined : connectorById.get(r.connectorId);
587
- const prefix = app ? `${app.slug}__` : undefined;
601
+ const prefix = app ? connectorToolKey(app.slug, "") : undefined;
588
602
  return [
589
603
  {
590
604
  ...r,
@@ -592,6 +606,12 @@ let ApprovalsService = class ApprovalsService {
592
606
  ? r.toolKey.slice(prefix.length)
593
607
  : r.toolKey,
594
608
  connector: app ? { id: app.id, name: app.name, slug: app.slug } : null,
609
+ extension: (() => {
610
+ const p = pluginOf(r);
611
+ return p === null
612
+ ? null
613
+ : { name: p, displayName: displayNames.get(p) ?? { zh: p, en: p } };
614
+ })(),
595
615
  agent: { id: c.agent_id, name: names.get(c.agent_id) ?? "" },
596
616
  ...(c.parent_conversation_id !== null
597
617
  ? {
@@ -0,0 +1,91 @@
1
+ import type { AgentToolRef } from "../agents/definition.js";
2
+ import type { ToolKind } from "./info.js";
3
+ /**
4
+ * The names (model-visible keys) of the kernel's builtin tools, following Claude Code (tool naming
5
+ * spec §4.1). Plain constants: `BuiltinTools`, the tool factories, the registry, policy and the
6
+ * runtime import them from here. The web keeps its own copy (`apps/web/src/lib/agents.ts`,
7
+ * `lib/timeline.ts`), checked equal by `test/tools/builtin-names.test.ts`. The release-candidate
8
+ * names are in `legacy-keys.ts`, never here.
9
+ */
10
+ export declare const BUILTIN: {
11
+ readonly Bash: "Bash";
12
+ readonly Read: "Read";
13
+ readonly Write: "Write";
14
+ readonly Edit: "Edit";
15
+ readonly Glob: "Glob";
16
+ readonly Grep: "Grep";
17
+ readonly Deliver: "Deliver";
18
+ readonly Skill: "Skill";
19
+ readonly SkillWrite: "SkillWrite";
20
+ readonly AskUserQuestion: "AskUserQuestion";
21
+ readonly ReadToolOutput: "ReadToolOutput";
22
+ readonly ToolSearch: "ToolSearch";
23
+ readonly Agent: "Agent";
24
+ readonly TaskOutput: "TaskOutput";
25
+ readonly TaskStop: "TaskStop";
26
+ readonly WebSearch: "WebSearch";
27
+ readonly WebFetch: "WebFetch";
28
+ };
29
+ export type BuiltinName = (typeof BUILTIN)[keyof typeof BUILTIN];
30
+ /**
31
+ * The builtin tools that run in the conversation's sandbox. `TaskOutput` and `TaskStop` read and
32
+ * stop the background tasks `Bash` started (background Bash spec §2.2, §2.3).
33
+ */
34
+ export declare const SANDBOX_TOOL_NAMES: readonly BuiltinName[];
35
+ /** The builtin tools that reach the web through the org's web-search / web-fetch providers. */
36
+ export declare const WEB_TOOL_NAMES: readonly BuiltinName[];
37
+ /**
38
+ * The tools offered with `Bash` whenever it is offered (background Bash: a model that can start a
39
+ * background task can read and stop it). `ToolRegistry` adds them; an explicit ref is harmless.
40
+ */
41
+ export declare const BASH_COMPANION_TOOLS: readonly BuiltinName[];
42
+ /**
43
+ * Sandbox tools that need approval when the org setting `sandboxToolPolicy` is `ask` (not the
44
+ * read-only ones). Not `TaskOutput` or `TaskStop`: they only read or stop a task whose launch
45
+ * already went through `Bash`'s policy (background Bash spec §4.5).
46
+ */
47
+ export declare const SANDBOX_ASK_TOOL_NAMES: ReadonlySet<string>;
48
+ /**
49
+ * Every builtin name. Saving an agent checks builtin refs against this list, not against what is
50
+ * registered now, so the order the implementations land in never affects a saved definition.
51
+ * `SkillWrite` is available only when a definition names it; `ToolSearch` and `Skill` are added
52
+ * by the registry; `Agent` dispatches sub-agents and its description is built per agent.
53
+ */
54
+ export declare const BUILTIN_TOOL_NAMES: readonly BuiltinName[];
55
+ /** The builtin that loads a skill (added by the registry when the log has a skills catalog). */
56
+ export declare const SKILL_LOAD: "Skill";
57
+ /** The builtin that searches the tool catalog (added by the registry by org setting and threshold). */
58
+ export declare const TOOL_SEARCH: "ToolSearch";
59
+ /** The builtin that dispatches sub-agents. */
60
+ export declare const AGENT_TOOL: "Agent";
61
+ /**
62
+ * Tool keys on the spec 3.6 R3 fixed list ("allowed is not confirmed"): their policy is at least
63
+ * `ask` and remembered grants never let them through. `SkillWrite` changes shared skills.
64
+ * Connector and extension tools express the same rule with `annotations.requiresUserInteraction`.
65
+ */
66
+ export declare const R3_FIXED_ASK_TOOL_KEYS: ReadonlySet<string>;
67
+ /**
68
+ * R3 keys that apply only to shared agents: a private agent's skills are not shared, so its
69
+ * `SkillWrite` takes effect without asking (R4).
70
+ */
71
+ export declare const R3_SHARED_AGENT_ONLY_TOOL_KEYS: ReadonlySet<string>;
72
+ /**
73
+ * Tools a sub-agent never gets (sub-agents spec §5.3, §5.5): `Agent` (depth 1), `AskUserQuestion`
74
+ * (the person does not see the child), `Deliver` (delivering files is the parent's job) and
75
+ * `SkillWrite`.
76
+ */
77
+ export declare const CHILD_REMOVED_TOOLS: readonly ["Agent", "AskUserQuestion", "Deliver", "SkillWrite"];
78
+ /**
79
+ * The tools of a new agent created without `tools` (`ToolSearch` is added by the registry, never
80
+ * by a definition). Only for create requests; updates and stored versions get no defaults.
81
+ */
82
+ export declare const DEFAULT_AGENT_TOOLS: AgentToolRef[];
83
+ /** Kinds of the builtin tools (display only); a builtin missing here is a plain `tool`. */
84
+ export declare const BUILTIN_TOOL_KINDS: Readonly<Record<string, ToolKind>>;
85
+ /**
86
+ * Input fields left out when matching a replayed call with a recorded one (full-run replay), per
87
+ * tool: display-only arguments the model writes that do not change what the tool does. `Bash`'s
88
+ * `description` became required in UI refresh B, so a recording without it and a replayed call
89
+ * with one (or a different wording) still match.
90
+ */
91
+ export declare const MATCH_IGNORED_FIELDS: Readonly<Record<string, readonly string[]>>;