@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
@@ -8,11 +8,14 @@ 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 { getQueueToken } from "@nestjs/bullmq";
11
12
  import { Inject, Injectable } from "@nestjs/common";
12
13
  import { sql } from "kysely";
13
14
  import { visibleTo } from "../agents/agent-lookup.js";
15
+ import { BG_WAKE_JOB, bgWakeJobId, wakeDecisionIn, } from "../background/wake-rules.js";
14
16
  import { EventLog } from "../conversations/event-log.js";
15
17
  import { dispatchQueuedIn, pauseQueueWith } from "../conversations/queue.js";
18
+ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
16
19
  import { KernelDb } from "../infra/database/kernel-db.js";
17
20
  import { TransactionHost } from "../infra/database/transaction-host.js";
18
21
  import { Transactional } from "../infra/database/transactional.decorator.js";
@@ -21,6 +24,7 @@ import { KERNEL_LOGGER } from "../infra/tokens.js";
21
24
  import { ToolPipeline } from "../tools/pipeline.js";
22
25
  import { KyselyCheckpointSaver } from "./checkpointer.js";
23
26
  import { ConversationLock } from "./conversation-lock.js";
27
+ import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
24
28
  import { notifyFinished, RunFinish } from "./run-finish.js";
25
29
  import { ACTIVE_RUN_STATUSES, RunJobs, runJobId, runQueueNameFor, } from "./run-jobs.js";
26
30
  export { ACTIVE_RUN_STATUSES, RUN_QUEUE_NAME, runJobId, runQueueNameFor, SUBAGENT_RUN_QUEUE_NAME, } from "./run-jobs.js";
@@ -29,6 +33,12 @@ export { ACTIVE_RUN_STATUSES, RUN_QUEUE_NAME, runJobId, runQueueNameFor, SUBAGEN
29
33
  * 只有进入 queued 超过这么久(`COALESCE(queued_at, created_at)`)仍没有任务的 queued 行才视为孤儿。
30
34
  */
31
35
  const ORPHAN_QUEUED_GRACE_SECONDS = 30;
36
+ /**
37
+ * Margin added to the delayed wake job: its window is read on the database's clock, the delay runs
38
+ * on this process's and Redis's, so the job does not usually fire just before the window ends (one
39
+ * that does reschedules itself under its next retry id, `bgWakeJobId`).
40
+ */
41
+ const WAKE_JOB_MARGIN_MS = 1000;
32
42
  /**
33
43
  * 运行队列(生产方):`ensureRun` 在会话有未消费的用户消息、且没有进行中的运行时插入 `runs` 行并投递任务。
34
44
  * 「是否已有运行」以 PG 为准(会话行锁内检查 `runs` 中的 queued / running),并参考会话锁的持有者;
@@ -45,11 +55,15 @@ let RunQueue = class RunQueue {
45
55
  events;
46
56
  log;
47
57
  txHost;
58
+ config;
59
+ maintenance;
48
60
  constructor(kdb, eventLog, lock, jobs, finisher,
49
61
  /** 运行以孤儿结束(标记失败 / 直接中止)后删除它的 checkpoint thread(thread_id = runId)。 */
50
62
  checkpoints,
51
- /** The run's open `agent` call results, in the orphan repair's transaction (sub-agents spec §12). */
52
- pipeline, events, log, txHost) {
63
+ /** The run's open `Agent` call results, in the orphan repair's transaction (sub-agents spec §12). */
64
+ pipeline, events, log, txHost, config,
65
+ /** The producer side of the delayed `bg-wake` job (`MaintenanceProcessor` consumes it). */
66
+ maintenance) {
53
67
  this.kdb = kdb;
54
68
  this.eventLog = eventLog;
55
69
  this.lock = lock;
@@ -60,12 +74,14 @@ let RunQueue = class RunQueue {
60
74
  this.events = events;
61
75
  this.log = log;
62
76
  this.txHost = txHost;
77
+ this.config = config;
78
+ this.maintenance = maintenance;
63
79
  }
64
80
  get db() {
65
81
  return this.kdb.db;
66
82
  }
67
83
  /**
68
- * The `beforeTerminal` of an orphan repair: writes the run's open `agent` call results in the
84
+ * The `beforeTerminal` of an orphan repair: writes the run's open `Agent` call results in the
69
85
  * repair's transaction; `fire()` runs their observe hooks after the commit (review m1).
70
86
  */
71
87
  stopOpenCalls(runId) {
@@ -81,6 +97,11 @@ let RunQueue = class RunQueue {
81
97
  * 有未消费的用户消息(最新 user_message 的 seq > `consumed_seq`)、会话锁无人持有、
82
98
  * 且没有 queued / running / waiting 的运行时:插入 `runs`(queued)并投递任务,返回 runId;否则返回 null。
83
99
  *
100
+ * The boundary (`trigger_seq`) is that newest message; with none unconsumed, the queue's head
101
+ * once it is sent; with no head either, a wake-eligible finished background task (background
102
+ * Bash spec §3, {@link wakeDecisionIn}): the run is then attributed to the task's trigger
103
+ * account, and a finish inside the wake window schedules the delayed `bg-wake` job instead.
104
+ *
84
105
  * 孤儿清理(R29,按需进行,没有定时器):已有的 queued / running 运行若 BullMQ 任务已不存在、
85
106
  * 会话锁也无人持有(queued 的还要超过宽限期),说明没有任何 worker 会再执行它——写
86
107
  * `run_failed { code: "run.orphaned" }` 把它标记为失败(consumed_seq 推进到它的触发消息),
@@ -89,7 +110,9 @@ let RunQueue = class RunQueue {
89
110
  *
90
111
  * agent 已软删除的会话不起新运行(孤儿清理照常进行)。
91
112
  */
92
- async ensureRun(conversationId) {
113
+ async ensureRun(conversationId,
114
+ /** From the `bg-wake` job: it fired `wakeRetry - 1` times before its window ended. */
115
+ opts = {}) {
93
116
  if ((await this.lock.holder(conversationId)) !== null)
94
117
  return null;
95
118
  const st = {
@@ -97,11 +120,17 @@ let RunQueue = class RunQueue {
97
120
  orphanRunId: null,
98
121
  dispatchedSeq: null,
99
122
  dispatchedSwitch: false,
123
+ dispatchedUnarchived: false,
100
124
  pausedNow: false,
101
125
  orphanStop: undefined,
126
+ wakeLater: null,
102
127
  };
103
128
  const created = await this.ensureRunIn(conversationId, st);
129
+ if (st.wakeLater !== null) {
130
+ await this.scheduleWake(conversationId, st.wakeLater, opts.wakeRetry ?? 0);
131
+ }
104
132
  const { orphanSeq, orphanRunId, dispatchedSeq, dispatchedSwitch, pausedNow, orphanStop } = st;
133
+ const { dispatchedUnarchived } = st;
105
134
  if (orphanSeq !== null) {
106
135
  await notifyFinished(this.eventLog, conversationId, orphanSeq, "failed");
107
136
  orphanStop?.fire();
@@ -114,6 +143,8 @@ let RunQueue = class RunQueue {
114
143
  await this.eventLog.publishChanged(conversationId, "queue");
115
144
  if (dispatchedSwitch)
116
145
  await this.eventLog.publishChanged(conversationId, "selection");
146
+ if (dispatchedUnarchived)
147
+ await this.eventLog.publishChanged(conversationId, "archived");
117
148
  }
118
149
  if (orphanRunId !== null) {
119
150
  await this.dropCheckpoints(orphanRunId);
@@ -153,14 +184,25 @@ let RunQueue = class RunQueue {
153
184
  const t = this.db;
154
185
  const conv = await t
155
186
  .selectFrom("conversations")
156
- .select(["consumed_seq", "agent_id", "kind", "queue_paused", "account_id"])
187
+ .select([
188
+ "consumed_seq",
189
+ "org_id",
190
+ "agent_id",
191
+ "kind",
192
+ "queue_paused",
193
+ "account_id",
194
+ "deleted_at",
195
+ ])
157
196
  .where("id", "=", conversationId)
158
197
  .forUpdate()
159
198
  .executeTakeFirst();
160
199
  // 评测回放的隐藏对话:运行由评测 worker 在里面直接驱动(工具只回放记录),绝不为它起真实运行
161
200
  if (conv?.kind !== "chat")
162
201
  return null;
163
- let last = await t
202
+ // A deleted conversation starts no run, whatever is still unconsumed or queued
203
+ if (conv.deleted_at !== null)
204
+ return null;
205
+ const newest = await t
164
206
  .selectFrom("conversation_events")
165
207
  .select(["seq", "payload"])
166
208
  .where("conversation_id", "=", conversationId)
@@ -168,11 +210,25 @@ let RunQueue = class RunQueue {
168
210
  .orderBy("seq", "desc")
169
211
  .limit(1)
170
212
  .executeTakeFirst();
171
- if (!last || Number(last.seq) <= Number(conv.consumed_seq)) {
213
+ /** The run's boundary: its `trigger_seq` and the account it runs as. */
214
+ let last = newest && {
215
+ seq: newest.seq,
216
+ accountId: newest.payload.authorAccountId,
217
+ };
218
+ const idleActive = !last || Number(last.seq) <= Number(conv.consumed_seq)
219
+ ? await this.activeRun(t, conversationId)
220
+ : undefined;
221
+ if (idleActive && Number(idleActive.trigger_seq) > Number(conv.consumed_seq)) {
222
+ // No unconsumed message, but a run whose boundary is not consumed yet (a wake run, whose
223
+ // boundary is a finished background task): it goes through the orphan check below with
224
+ // that boundary, as a message's run does
225
+ last = { seq: idleActive.trigger_seq, accountId: idleActive.trigger_account_id };
226
+ }
227
+ else if (!last || Number(last.seq) <= Number(conv.consumed_seq)) {
172
228
  // Nothing unconsumed: the queue's head goes out when the conversation is idle (no active
173
229
  // run, which includes runs waiting for approval or an answer) and the queue is not paused.
174
230
  // Interjections are always unconsumed messages, so the queue never jumps ahead of them.
175
- if (conv.queue_paused || (await this.activeRun(t, conversationId)))
231
+ if (conv.queue_paused || idleActive)
176
232
  return null;
177
233
  const owner = await t
178
234
  .selectFrom("agents")
@@ -187,32 +243,46 @@ let RunQueue = class RunQueue {
187
243
  .where("conversation_id", "=", conversationId)
188
244
  .limit(1)
189
245
  .executeTakeFirst();
190
- if (!head)
191
- return null;
192
- // A queued message may wait for hours: it goes out only while its author can still use
193
- // the conversation (active, a member, the agent visible to them; the isAccessible rule).
194
- // Otherwise the queue pauses instead of posting as someone who lost access.
195
- if (!(await authorCanSend(t, conversationId, conv.account_id))) {
196
- await pauseQueueWith(t, conversationId, "author_cannot_send");
197
- st.pausedNow = true;
198
- this.log.warn("queued message not sent: its author can no longer use the conversation", {
199
- conversationId,
200
- });
201
- return null;
246
+ if (head) {
247
+ // A queued message may wait for hours: it goes out only while its author can still use
248
+ // the conversation (active, a member, the agent visible to them; the isAccessible rule).
249
+ // Otherwise the queue pauses instead of posting as someone who lost access.
250
+ if (!(await authorCanSend(t, conversationId, conv.account_id))) {
251
+ await pauseQueueWith(t, conversationId, "author_cannot_send");
252
+ st.pausedNow = true;
253
+ this.log.warn("queued message not sent: its author can no longer use the conversation", {
254
+ conversationId,
255
+ });
256
+ return null;
257
+ }
258
+ const sent = await dispatchQueuedIn(t, this.eventLog, conversationId);
259
+ if (!sent)
260
+ return null;
261
+ if ("paused" in sent) {
262
+ st.pausedNow = true;
263
+ this.log.warn("queued skill invocation not sent: the skill can no longer be invoked", {
264
+ conversationId,
265
+ });
266
+ return null;
267
+ }
268
+ st.dispatchedSeq = sent.event.seq;
269
+ st.dispatchedSwitch = sent.switched;
270
+ st.dispatchedUnarchived = sent.unarchived;
271
+ last = {
272
+ seq: String(sent.event.seq),
273
+ accountId: sent.event.payload.authorAccountId,
274
+ };
202
275
  }
203
- const sent = await dispatchQueuedIn(t, this.eventLog, conversationId);
204
- if (!sent)
205
- return null;
206
- if ("paused" in sent) {
207
- st.pausedNow = true;
208
- this.log.warn("queued skill invocation not sent: the skill can no longer be invoked", {
209
- conversationId,
210
- });
211
- return null;
276
+ else {
277
+ // Nothing queued either: a finished background task may wake the idle conversation
278
+ // (background Bash spec §3; a person's message or the queue always goes first)
279
+ const wake = await wakeDecisionIn(t, { id: conversationId, orgId: conv.org_id }, Number(conv.consumed_seq), this.config.get().runtime.wakeOnBackgroundTask, (accountId) => authorCanSend(t, conversationId, accountId));
280
+ if (wake?.kind === "later")
281
+ st.wakeLater = wake;
282
+ if (wake?.kind !== "wake")
283
+ return null;
284
+ last = { seq: String(wake.seq), accountId: wake.triggerAccountId };
212
285
  }
213
- st.dispatchedSeq = sent.event.seq;
214
- st.dispatchedSwitch = sent.switched;
215
- last = { seq: String(sent.event.seq), payload: sent.event.payload };
216
286
  }
217
287
  const active = await this.activeRun(t, conversationId);
218
288
  if (active) {
@@ -254,7 +324,9 @@ let RunQueue = class RunQueue {
254
324
  .orderBy("seq")
255
325
  .limit(1)
256
326
  .executeTakeFirst();
257
- const trigger = fork ?? last;
327
+ const trigger = fork
328
+ ? { seq: fork.seq, accountId: fork.payload.authorAccountId }
329
+ : last;
258
330
  // BullMQ 对重复的 jobId 静默忽略(add 返回的 Job 由调用方数据构造,看不出是否真的入队):
259
331
  // 先显式查同一触发点的任务;仍存在(例如属于刚结束、尚未从队列移除的运行)就不插入 runs 行。
260
332
  const jobId = runJobId(conversationId, Number(trigger.seq));
@@ -283,12 +355,33 @@ let RunQueue = class RunQueue {
283
355
  conversation_id: conversationId,
284
356
  status: "queued",
285
357
  agent_version: agent.current_version,
286
- trigger_account_id: trigger.payload.authorAccountId,
358
+ trigger_account_id: trigger.accountId,
287
359
  trigger_seq: Number(trigger.seq),
288
360
  })
289
361
  .execute();
290
362
  return { runId, jobId };
291
363
  }
364
+ /**
365
+ * Schedules the delayed `bg-wake` job at the end of the conversation's wake window (after the
366
+ * commit): one job per conversation and window end, so finishes inside one window share it. It
367
+ * calls {@link ensureRun}, which wakes the conversation if a finish is still unconsumed. A failed
368
+ * enqueue is logged: the next message, run end or page opening re-derives the wake.
369
+ */
370
+ async scheduleWake(conversationId, w, retry) {
371
+ const data = { conversationId, ...(retry > 0 && { retry }) };
372
+ try {
373
+ await this.maintenance.add(BG_WAKE_JOB, data, {
374
+ // a job that fired early reschedules under a new id (its own is still taken)
375
+ jobId: bgWakeJobId(conversationId, w.windowEnd, retry),
376
+ delay: w.delayMs + WAKE_JOB_MARGIN_MS,
377
+ removeOnComplete: true,
378
+ removeOnFail: true,
379
+ });
380
+ }
381
+ catch (err) {
382
+ this.log.error("cannot schedule the background task wake", { conversationId, err });
383
+ }
384
+ }
292
385
  /**
293
386
  * Enqueues a run job under the caller's job id (resumes, dispatches, repairs), with the same
294
387
  * options as {@link ensureRun} (see {@link RunJobs.enqueue}).
@@ -407,6 +500,7 @@ let RunQueue = class RunQueue {
407
500
  "id",
408
501
  "status",
409
502
  "trigger_seq",
503
+ "trigger_account_id",
410
504
  "job_id",
411
505
  sql `COALESCE(queued_at, created_at) < now() - make_interval(secs => ${ORPHAN_QUEUED_GRACE_SECONDS})`.as("past_grace"),
412
506
  ])
@@ -462,7 +556,9 @@ RunQueue = __decorate([
462
556
  __param(6, Inject(ToolPipeline)),
463
557
  __param(7, Inject(KernelEvents)),
464
558
  __param(8, Inject(KERNEL_LOGGER)),
465
- __param(9, Inject(TransactionHost))
559
+ __param(9, Inject(TransactionHost)),
560
+ __param(10, Inject(KernelConfigService)),
561
+ __param(11, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
466
562
  ], RunQueue);
467
563
  export { RunQueue };
468
564
  /** The conversation's owner is active and can still see its agent (`isAccessible`'s rule). */
@@ -63,7 +63,7 @@ export declare class RunWorker {
63
63
  /** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
64
64
  private readonly skillsCatalog;
65
65
  /**
66
- * In the terminal transaction of every run: writes its open `agent` call results
66
+ * In the terminal transaction of every run: writes its open `Agent` call results
67
67
  * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
68
68
  * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
69
69
  */
@@ -94,7 +94,7 @@ export declare class RunWorker {
94
94
  /** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
95
95
  skillsCatalog: Pick<SkillsCatalogService, "record">,
96
96
  /**
97
- * In the terminal transaction of every run: writes its open `agent` call results
97
+ * In the terminal transaction of every run: writes its open `Agent` call results
98
98
  * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
99
99
  * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
100
100
  */
@@ -10,8 +10,8 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { Inject, Injectable, Optional } from "@nestjs/common";
11
11
  import { DelayedError } from "bullmq";
12
12
  import { sql } from "kysely";
13
- import { normalizeStoredDefinition } from "../agents/definition.js";
14
13
  import { conversationTopic, EventLog, } from "../conversations/event-log.js";
14
+ import { isAbsorbable, } from "../conversations/events.js";
15
15
  import { effectiveDefinition, NO_SELECTION, readSelection, sameModel, sameSelection, } from "../conversations/selection.js";
16
16
  import { TitleQueue } from "../conversations/titles.js";
17
17
  import { ApiError } from "../errors.js";
@@ -24,6 +24,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
24
24
  import { KernelEvents } from "../infra/events/kernel-events.js";
25
25
  import { KERNEL_BUS, KERNEL_LOGGER } from "../infra/tokens.js";
26
26
  import { pinnedFailureText, SkillsCatalogService } from "../skills/catalog.js";
27
+ import { normalizeStoredDefinitionIn } from "../tools/legacy-data.js";
27
28
  import { ToolPipeline } from "../tools/pipeline.js";
28
29
  import { RunAbort } from "./abort.js";
29
30
  import { KyselyCheckpointSaver } from "./checkpointer.js";
@@ -109,7 +110,7 @@ let RunWorker = class RunWorker {
109
110
  /** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
110
111
  skillsCatalog,
111
112
  /**
112
- * In the terminal transaction of every run: writes its open `agent` call results
113
+ * In the terminal transaction of every run: writes its open `Agent` call results
113
114
  * (`stopOpenAgentCalls`; a no-op without dispatches); `fire` runs their observe hooks after the
114
115
  * commit (sub-agents spec §12, review m1: a failed or stalled parent too).
115
116
  */
@@ -525,7 +526,7 @@ let RunWorker = class RunWorker {
525
526
  // a follow-up runs with its task message's narrowing (final review I1): the frozen tools
526
527
  // and org skills the current version still allows, recorded at its dispatch
527
528
  const trigger = (await this.eventLog.readAfter(conversationId, Number(run.trigger_seq) - 1, 1))[0];
528
- agent = childRunDefinition(normalizeStoredDefinition(started.payload.definition), trigger?.type === "user_message" && trigger.seq === Number(run.trigger_seq)
529
+ agent = childRunDefinition(await normalizeStoredDefinitionIn(this.db, conv.org_id, started.payload.definition), trigger?.type === "user_message" && trigger.seq === Number(run.trigger_seq)
529
530
  ? trigger.payload.dispatch?.narrowed
530
531
  : undefined);
531
532
  maxSteps = Math.min(started.payload.maxSteps, runtime.maxSteps);
@@ -552,7 +553,7 @@ let RunWorker = class RunWorker {
552
553
  .where("agent_id", "=", conv.agent_id)
553
554
  .where("version", "=", run.agent_version)
554
555
  .executeTakeFirstOrThrow();
555
- saved = normalizeStoredDefinition(version.definition);
556
+ saved = await normalizeStoredDefinitionIn(this.db, conv.org_id, version.definition);
556
557
  agent = saved;
557
558
  }
558
559
  let startedAt = run.started_at;
@@ -642,15 +643,16 @@ let RunWorker = class RunWorker {
642
643
  .executeTakeFirstOrThrow();
643
644
  const startConsumed = Number(consumed_seq);
644
645
  let absorbed = startConsumed;
645
- const pendingUserMessages = async (afterSeq) => {
646
+ const pendingAbsorbable = async (afterSeq) => {
646
647
  // loop 声明已吸收到 afterSeq(续跑时来自 checkpoint,可能包含之前进程吸收的消息)。
647
648
  absorbed = Math.max(absorbed, afterSeq);
648
649
  const out = [];
649
650
  let after = Math.max(afterSeq, startConsumed);
650
651
  for (;;) {
651
652
  const page = await this.eventLog.readAfter(conversationId, after, READ_PAGE);
653
+ // messages and finished background tasks (spec 2026-11-05-background-bash-design §2.4)
652
654
  for (const e of page)
653
- if (e.type === "user_message")
655
+ if (isAbsorbable(e))
654
656
  out.push(e);
655
657
  const last = page.at(-1);
656
658
  if (page.length < READ_PAGE || !last)
@@ -677,7 +679,7 @@ let RunWorker = class RunWorker {
677
679
  signal: ac.signal,
678
680
  shutdown: state.shutdown.signal,
679
681
  sink: this.sinkFor(conversationId),
680
- pendingUserMessages,
682
+ pendingAbsorbable,
681
683
  limits: { maxSteps, deadline },
682
684
  // a sub-agent's conversation fires hooks like a chat, with `parent` set (spec §11)
683
685
  conversationKind: conv.kind === "eval" ? "eval" : conv.kind === "subagent" ? "subagent" : "chat",
@@ -814,6 +816,13 @@ let RunWorker = class RunWorker {
814
816
  const agent = saved !== undefined
815
817
  ? effectiveDefinition(saved, await readSelection(t, conversationId))
816
818
  : current;
819
+ // a wake run's boundary is a finished background task, not a message (background Bash §3)
820
+ const boundary = await t
821
+ .selectFrom("conversation_events")
822
+ .select("type")
823
+ .where("conversation_id", "=", conversationId)
824
+ .where("seq", "=", run.trigger_seq)
825
+ .executeTakeFirst();
817
826
  const [ev] = await this.eventLog.appendIn(conversationId, [
818
827
  {
819
828
  type: "run_started",
@@ -823,6 +832,10 @@ let RunWorker = class RunWorker {
823
832
  agentVersion: run.agent_version,
824
833
  model: agent.model,
825
834
  triggerAccountId: run.trigger_account_id,
835
+ ...(boundary?.type === "background_task_finished" && {
836
+ trigger: "background_task",
837
+ triggerSeq: Number(run.trigger_seq),
838
+ }),
826
839
  },
827
840
  },
828
841
  ]);
@@ -5,6 +5,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { Module } from "@nestjs/common";
8
+ import { BackgroundModule } from "../background/background.module.js";
9
+ import { BackgroundWake } from "../background/wake.js";
8
10
  import { ConversationAccessModule } from "../conversations/access.js";
9
11
  import { AttachmentsModule } from "../conversations/attachments.module.js";
10
12
  import { EventLogModule } from "../conversations/event-log.js";
@@ -28,7 +30,7 @@ import { RunJobsModule } from "./run-jobs.js";
28
30
  import { RunQueue } from "./run-queue.js";
29
31
  import { AgentLoops, RunSteps } from "./run-steps.js";
30
32
  import { RunWorker } from "./run-worker.js";
31
- import { StepLog } from "./step-log.js";
33
+ import { StepLogModule } from "./step-log.js";
32
34
  import { ConversationStreams } from "./stream.js";
33
35
  import { SubagentsModule } from "./subagents/subagents.service.js";
34
36
  import { UsageReportModule } from "./usage-report.js";
@@ -58,6 +60,8 @@ RuntimeModule = __decorate([
58
60
  SubagentsModule,
59
61
  UsageReportModule,
60
62
  TitlesModule,
63
+ StepLogModule,
64
+ BackgroundModule,
61
65
  // the producer side of `wait-timeout` jobs (`MaintenanceProcessor` consumes them)
62
66
  QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
63
67
  ],
@@ -67,7 +71,6 @@ RuntimeModule = __decorate([
67
71
  RunAbort,
68
72
  KyselyCheckpointSaver,
69
73
  RunFinish,
70
- StepLog,
71
74
  RunQueue,
72
75
  Waits,
73
76
  DecisionResumer,
@@ -75,6 +78,8 @@ RuntimeModule = __decorate([
75
78
  AgentLoops,
76
79
  RunSteps,
77
80
  RunWorker,
81
+ // a finished background task wakes an idle conversation (background Bash spec §3)
82
+ BackgroundWake,
78
83
  // one quota per key prefix (the org run quota, the org eval quota)
79
84
  {
80
85
  provide: RUN_QUOTA,
@@ -93,7 +98,7 @@ RuntimeModule = __decorate([
93
98
  RunAbort,
94
99
  KyselyCheckpointSaver,
95
100
  RunFinish,
96
- StepLog,
101
+ StepLogModule,
97
102
  RunQueue,
98
103
  Waits,
99
104
  RunControl,
@@ -38,3 +38,9 @@ export declare class StepLog {
38
38
  spec: ToolSpec;
39
39
  }[], events: AppendEvent[]): Promise<ConversationEvent[]>;
40
40
  }
41
+ /**
42
+ * The step writes ({@link StepLog}): a module of its own, so the tools domain (the `WebFetch`
43
+ * extraction's usage and denials) uses them without importing the runtime.
44
+ */
45
+ export declare class StepLogModule {
46
+ }
@@ -7,9 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Inject, Injectable } from "@nestjs/common";
10
+ import { Inject, Injectable, Module } from "@nestjs/common";
11
11
  import { sql } from "kysely";
12
- import { EventLog } from "../conversations/event-log.js";
12
+ import { EventLog, EventLogModule } from "../conversations/event-log.js";
13
13
  import { usageTokens } from "../evals/budget.js";
14
14
  import { audit } from "../identity/audit.js";
15
15
  import { KernelDb } from "../infra/database/kernel-db.js";
@@ -92,3 +92,17 @@ StepLog = __decorate([
92
92
  __param(2, Inject(TransactionHost))
93
93
  ], StepLog);
94
94
  export { StepLog };
95
+ /**
96
+ * The step writes ({@link StepLog}): a module of its own, so the tools domain (the `WebFetch`
97
+ * extraction's usage and denials) uses them without importing the runtime.
98
+ */
99
+ let StepLogModule = class StepLogModule {
100
+ };
101
+ StepLogModule = __decorate([
102
+ Module({
103
+ imports: [EventLogModule],
104
+ providers: [StepLog],
105
+ exports: [StepLog],
106
+ })
107
+ ], StepLogModule);
108
+ export { StepLogModule };
@@ -6,11 +6,7 @@ import type { KernelConfigService } from "../../infra/config/kernel-config.servi
6
6
  import type { ModelPurposesService } from "../../models/purposes.js";
7
7
  import type { ProviderRegistry } from "../providers.js";
8
8
  import type { RosterEntry } from "./roster.js";
9
- /**
10
- * Tools a sub-agent never gets (spec §5.3, §5.5): `agent` (depth 1), `ask_user` (the person does not
11
- * see the child), `sandbox_deliver` (delivering files is the parent's job) and `skill_write`.
12
- */
13
- export declare const CHILD_REMOVED_TOOLS: readonly ["agent", "ask_user", "sandbox_deliver", "skill_write"];
9
+ export { CHILD_REMOVED_TOOLS } from "../../tools/builtin-names.js";
14
10
  /**
15
11
  * The roster entry's template is gone (an inline template edited away, a plugin template
16
12
  * uninstalled) between resolving the roster and reading it. The dispatch refuses the call as an
@@ -100,4 +96,3 @@ export declare function intersectTools(frozen: readonly AgentToolRef[], current:
100
96
  export declare function narrowFollowUp(frozen: Pick<AgentDefinition, "tools" | "skills">, current: Pick<AgentDefinition, "tools" | "skills">): FollowUpNarrowing;
101
97
  /** The definition a child run uses: the frozen one with its trigger's follow-up narrowing. */
102
98
  export declare function childRunDefinition(frozen: AgentDefinition, narrowed: FollowUpNarrowing | undefined): AgentDefinition;
103
- export {};
@@ -1,13 +1,9 @@
1
- import { SKILL_ENTRY_FILE, sanitizeParams, } from "@capybara-hq/plugin-sdk";
1
+ import { connectorToolKey, extensionToolKey, parseToolKey, SKILL_ENTRY_FILE, sanitizeParams, } from "@capybara-hq/plugin-sdk";
2
2
  import { loadTemplates } from "../../agents/templates.js";
3
3
  import { ApiError } from "../../errors.js";
4
4
  import { PINNED_SKILLS_LIMITS } from "../../skills/catalog.js";
5
- import { toolKey } from "../../tools/naming.js";
6
- /**
7
- * Tools a sub-agent never gets (spec §5.3, §5.5): `agent` (depth 1), `ask_user` (the person does not
8
- * see the child), `sandbox_deliver` (delivering files is the parent's job) and `skill_write`.
9
- */
10
- export const CHILD_REMOVED_TOOLS = ["agent", "ask_user", "sandbox_deliver", "skill_write"];
5
+ import { CHILD_REMOVED_TOOLS } from "../../tools/builtin-names.js";
6
+ export { CHILD_REMOVED_TOOLS } from "../../tools/builtin-names.js";
11
7
  /**
12
8
  * The roster entry's template is gone (an inline template edited away, a plugin template
13
9
  * uninstalled) between resolving the roster and reading it. The dispatch refuses the call as an
@@ -72,7 +68,7 @@ async function loadConnectors(db, orgId, refs) {
72
68
  /**
73
69
  * Narrows tool refs by resolved key (spec §5.3): keeps a key when `allow` (if given) lists it and
74
70
  * neither `deny` nor {@link CHILD_REMOVED_TOOLS} does. A connector ref with `tools: "all"` stays
75
- * live only when nothing can narrow it (no allowlist and no deny key under `<slug>__`), so a tool
71
+ * live only when nothing can narrow it (no allowlist and no deny key under `mcp__<slug>__`), so a tool
76
72
  * the connector gains later never slips past the template; otherwise it becomes the kept names, or
77
73
  * is dropped. A connector that no longer exists keeps its ref only without an allowlist.
78
74
  */
@@ -86,7 +82,7 @@ function narrowTools(refs, connectors, allow, deny) {
86
82
  out.push(ref);
87
83
  }
88
84
  else if (ref.kind === "plugin") {
89
- if (keep(toolKey(ref.plugin, ref.name)))
85
+ if (keep(extensionToolKey(ref.plugin, ref.name)))
90
86
  out.push(ref);
91
87
  }
92
88
  else {
@@ -96,13 +92,16 @@ function narrowTools(refs, connectors, allow, deny) {
96
92
  out.push(ref);
97
93
  continue;
98
94
  }
99
- const prefix = toolKey(c.slug, "");
100
- if (ref.tools === "all" && allow === null && ![...deny].some((k) => k.startsWith(prefix))) {
95
+ const ownsKey = (k) => {
96
+ const parsed = parseToolKey(k);
97
+ return parsed?.kind === "connector" && parsed.slug === c.slug;
98
+ };
99
+ if (ref.tools === "all" && allow === null && ![...deny].some(ownsKey)) {
101
100
  out.push(ref);
102
101
  continue;
103
102
  }
104
103
  const names = ref.tools === "all" ? c.tools : ref.tools;
105
- const kept = names.filter((name) => keep(toolKey(c.slug, name)));
104
+ const kept = names.filter((name) => keep(connectorToolKey(c.slug, name)));
106
105
  if (kept.length > 0)
107
106
  out.push({ ...ref, tools: kept });
108
107
  }
@@ -229,7 +228,9 @@ function orgAgentDefinition(d, a) {
229
228
  }
230
229
  const agentId = a.entry.agentId ?? "";
231
230
  const own = a.orgAgent.definition;
232
- const { subagents: _subagents, skills, ...rest } = own;
231
+ // prompt files are authoring data: the child's model sees only `instructions` (their join), and
232
+ // `subagent_started.definition` keeps its shape (spec 2026-11-04 §3.3)
233
+ const { subagents: _subagents, skills, instructionFiles: _files, ...rest } = own;
233
234
  const definition = {
234
235
  ...rest,
235
236
  // its own tools, not intersected with the parent's; only the child-removed ones go
@@ -48,7 +48,7 @@ export type DispatchOutcome = {
48
48
  };
49
49
  /**
50
50
  * A dispatch for a person's fork invocation (phase D spec §6.2, `advanceSkillForks`), not for a
51
- * model's `agent` call: `general-purpose` is always available (the `agent` tool and
51
+ * model's `Agent` call: `general-purpose` is always available (the `Agent` tool and
52
52
  * `subagents.general` do not gate it), an unknown type falls back to it with a warning, and the
53
53
  * child records the invoked skill.
54
54
  */
@@ -83,7 +83,7 @@ export interface DispatchPlan {
83
83
  tierFallback: boolean;
84
84
  }
85
85
  /**
86
- * Dispatches the sub-agent of one `agent` call (spec §7.1), after the pipeline's validation,
86
+ * Dispatches the sub-agent of one `Agent` call (spec §7.1), after the pipeline's validation,
87
87
  * policy, approval and `tool.preExecute` stages. A new sub-agent: resolves its type and effective
88
88
  * definition, then in one transaction (holding the parent run's row lock, so the dispatches of one
89
89
  * run serialize) inserts the child conversation, appends `subagent_started` and the task message,
@@ -20,7 +20,7 @@ const GENERAL_ENTRY = {
20
20
  /** Idempotency key of the parent's `subagent_dispatched` event. */
21
21
  export const dispatchedIdemKey = (runId, callId) => `subagent_dispatched:${runId}:${callId}`;
22
22
  /**
23
- * Dispatches the sub-agent of one `agent` call (spec §7.1), after the pipeline's validation,
23
+ * Dispatches the sub-agent of one `Agent` call (spec §7.1), after the pipeline's validation,
24
24
  * policy, approval and `tool.preExecute` stages. A new sub-agent: resolves its type and effective
25
25
  * definition, then in one transaction (holding the parent run's row lock, so the dispatches of one
26
26
  * run serialize) inserts the child conversation, appends `subagent_started` and the task message,
@@ -274,7 +274,7 @@ async function orgAgentGates(db, a, agentId, agentType, resumed) {
274
274
  }
275
275
  const taskMessage = (ctx, callId, prompt, narrowed,
276
276
  /**
277
- * A fork invocation's record: the child's log then holds the invocation, so its `skill_load`
277
+ * A fork invocation's record: the child's log then holds the invocation, so its `Skill`
278
278
  * is offered and resolves the invoked version (phase D).
279
279
  */
280
280
  skill) => ({