@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
@@ -0,0 +1,26 @@
1
+ import { type Principal } from "../identity/principal.js";
2
+ import { type OrgContext } from "../orgs/org.guard.js";
3
+ import { ProviderRegistry } from "../runtime/providers.js";
4
+ import { AgentsService } from "./agents.service.js";
5
+ /**
6
+ * Whether the org can run (`/api/v1/orgs/:orgId/readiness`, web-order spec §3.8): the web app
7
+ * decides between the shell and the setup layout with it. `modelProvider` is `ready` when a
8
+ * model-provider extension is loaded (enabled and configured), from extension state only, never
9
+ * a vendor call; `agents` is the number of agents the caller can see (the agent list's
10
+ * visibility). Humans and service tokens (`org.read`).
11
+ *
12
+ * `modelProvider` is deployment-wide, not about this org: model providers are not org-scoped
13
+ * (an org's model purposes map onto the deployment's providers but cannot add one), so every org
14
+ * of the deployment reads the same value. It is read from this replica's provider registry, which
15
+ * follows the per-replica extension reconcile: right after a provider is enabled, a replica that
16
+ * has not reconciled yet may still answer `none` for a moment.
17
+ */
18
+ export declare class ReadinessController {
19
+ private readonly registry;
20
+ private readonly agents;
21
+ constructor(registry: ProviderRegistry, agents: AgentsService);
22
+ get(principal: Principal, org: OrgContext): Promise<{
23
+ modelProvider: "ready" | "none";
24
+ agents: number;
25
+ }>;
26
+ }
@@ -0,0 +1,56 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Controller, Get, Inject } from "@nestjs/common";
11
+ import { CurrentPrincipal } from "../identity/principal.js";
12
+ import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
13
+ import { ProviderRegistry } from "../runtime/providers.js";
14
+ import { AgentsService } from "./agents.service.js";
15
+ /** Service tokens have no account: they see `visibility='org'` agents only (as the agent list). */
16
+ const viewerOf = (p) => (p.kind === "user" ? p.account.id : null);
17
+ /**
18
+ * Whether the org can run (`/api/v1/orgs/:orgId/readiness`, web-order spec §3.8): the web app
19
+ * decides between the shell and the setup layout with it. `modelProvider` is `ready` when a
20
+ * model-provider extension is loaded (enabled and configured), from extension state only, never
21
+ * a vendor call; `agents` is the number of agents the caller can see (the agent list's
22
+ * visibility). Humans and service tokens (`org.read`).
23
+ *
24
+ * `modelProvider` is deployment-wide, not about this org: model providers are not org-scoped
25
+ * (an org's model purposes map onto the deployment's providers but cannot add one), so every org
26
+ * of the deployment reads the same value. It is read from this replica's provider registry, which
27
+ * follows the per-replica extension reconcile: right after a provider is enabled, a replica that
28
+ * has not reconciled yet may still answer `none` for a moment.
29
+ */
30
+ let ReadinessController = class ReadinessController {
31
+ registry;
32
+ agents;
33
+ constructor(registry, agents) {
34
+ this.registry = registry;
35
+ this.agents = agents;
36
+ }
37
+ async get(principal, org) {
38
+ const visible = await this.agents.listVisible(org.orgId, viewerOf(principal));
39
+ return {
40
+ modelProvider: this.registry.hasProvider() ? "ready" : "none",
41
+ agents: visible.length,
42
+ };
43
+ }
44
+ };
45
+ __decorate([
46
+ Get(),
47
+ OrgAction("org.read"),
48
+ __param(0, CurrentPrincipal()),
49
+ __param(1, CurrentOrg())
50
+ ], ReadinessController.prototype, "get", null);
51
+ ReadinessController = __decorate([
52
+ Controller("api/v1/orgs/:orgId/readiness"),
53
+ __param(0, Inject(ProviderRegistry)),
54
+ __param(1, Inject(AgentsService))
55
+ ], ReadinessController);
56
+ export { ReadinessController };
@@ -10,7 +10,9 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { Inject, Injectable } from "@nestjs/common";
12
12
  import { KernelDb } from "../infra/database/kernel-db.js";
13
- import { canonicalJson } from "../tools/registry.js";
13
+ import { canonicalJson } from "../tools/canonical-json.js";
14
+ import { loadLegacyScope } from "../tools/legacy-data.js";
15
+ import { needsLegacyScope, normalizeToolKeys } from "./definition.js";
14
16
  const baseQuery = (db, orgId) => db
15
17
  .selectFrom("agent_templates as t")
16
18
  .innerJoin("agent_template_versions as v", (j) => j.onRef("v.template_id", "=", "t.id").onRef("v.version", "=", "t.current_version"))
@@ -26,16 +28,27 @@ const baseQuery = (db, orgId) => db
26
28
  ])
27
29
  .where("t.org_id", "=", orgId)
28
30
  .where("t.deleted_at", "is", null);
29
- const rowOf = (r) => ({
31
+ /**
32
+ * A stored template row; its tool keys read as current keys (tool naming spec §8.3): old builtin
33
+ * names always, rc `<x>__<tool>` keys with `scope` (see {@link scopedRows}).
34
+ */
35
+ const rowOf = (r, scope) => ({
30
36
  id: r.id,
31
37
  name: r.name,
32
38
  plugin: r.plugin_name,
33
39
  version: r.current_version,
34
- template: r.definition,
40
+ template: normalizeToolKeys(r.definition, scope),
35
41
  sourceFormat: r.source_format,
36
42
  catalogItemId: r.catalog_item_id,
37
43
  catalogVersion: r.catalog_version,
38
44
  });
45
+ /** The rows of one org as {@link TemplateRow}s, reading the org's legacy scope once when needed. */
46
+ async function scopedRows(db, orgId, rows) {
47
+ const scope = rows.some((r) => needsLegacyScope(r.definition))
48
+ ? await loadLegacyScope(db, orgId)
49
+ : undefined;
50
+ return rows.map((r) => rowOf(r, scope));
51
+ }
39
52
  /** The live templates of `ids` in the org (deleted and foreign ids are left out), by id. */
40
53
  export async function loadTemplates(db, orgId, ids) {
41
54
  if (ids.length === 0)
@@ -43,7 +56,7 @@ export async function loadTemplates(db, orgId, ids) {
43
56
  const rows = await baseQuery(db, orgId)
44
57
  .where("t.id", "in", [...ids])
45
58
  .execute();
46
- return new Map(rows.map((r) => [r.id, rowOf(r)]));
59
+ return new Map((await scopedRows(db, orgId, rows)).map((t) => [t.id, t]));
47
60
  }
48
61
  /** Reads the org's agent templates (consumed by the roster, the agent page and Task 19's API). */
49
62
  let AgentTemplatesService = class AgentTemplatesService {
@@ -84,7 +97,9 @@ let AgentTemplatesService = class AgentTemplatesService {
84
97
  const r = await q.executeTakeFirst();
85
98
  if (!r)
86
99
  return null;
87
- const t = rowOf(r);
100
+ const [t] = await scopedRows(this.db.db, orgId, [r]);
101
+ if (!t)
102
+ return null;
88
103
  return { id: t.id, version: t.version, plugin: t.plugin, template: t.template };
89
104
  }
90
105
  };
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ import { type Principal } from "../identity/principal.js";
3
+ import { type OrgContext } from "../orgs/org.guard.js";
4
+ import { type BackgroundTaskOutput, BackgroundTasks, type BackgroundTaskView } from "./tasks.service.js";
5
+ declare const ConversationParams: z.ZodObject<{
6
+ id: z.ZodString;
7
+ }, z.core.$strip>;
8
+ declare const TaskParams: z.ZodObject<{
9
+ id: z.ZodString;
10
+ taskId: z.ZodString;
11
+ }, z.core.$strip>;
12
+ declare const OutputQuery: z.ZodObject<{
13
+ tail: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
14
+ }, z.core.$strip>;
15
+ /**
16
+ * Background Bash tasks of a conversation (spec 2026-11-05-background-bash-design §5). Visible to
17
+ * whoever sees the conversation, stoppable by whoever may abort its run (the owner): the same
18
+ * checks as `GET :id` and `POST :id/abort`.
19
+ */
20
+ export declare class BackgroundController {
21
+ private readonly tasks;
22
+ constructor(tasks: BackgroundTasks);
23
+ /** The conversation's tasks and its sub-agents' (`subagentId`), newest first. */
24
+ list(principal: Principal, org: OrgContext, params: z.infer<typeof ConversationParams>): Promise<{
25
+ tasks: BackgroundTaskView[];
26
+ }>;
27
+ /**
28
+ * Stops a task like a person's stop: 200 `{ task }` with the row after the stop. A task that is
29
+ * still `running` with `stopping: true` is being stopped by another request (or is still
30
+ * launching); a stop that fails is an error and the task keeps running.
31
+ */
32
+ stop(principal: Principal, org: OrgContext, params: z.infer<typeof TaskParams>): Promise<{
33
+ task: BackgroundTaskView;
34
+ }>;
35
+ /** The end of the task's output (`tail` bytes, at most 65536); 409 when it cannot be read now. */
36
+ output(principal: Principal, org: OrgContext, params: z.infer<typeof TaskParams>, q: z.infer<typeof OutputQuery>): Promise<BackgroundTaskOutput>;
37
+ }
38
+ export {};
@@ -0,0 +1,87 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Controller, Get, HttpCode, Inject, Post } from "@nestjs/common";
11
+ import { z } from "zod";
12
+ import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
13
+ import { createZodDto } from "../infra/validation/create-zod-dto.js";
14
+ import { ZParam, ZQuery } from "../infra/validation/zod-params.js";
15
+ import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
16
+ import { BackgroundTasks, OUTPUT_TAIL_MAX_BYTES, } from "./tasks.service.js";
17
+ const ConversationParams = z.object({ id: z.string().min(1).max(100) });
18
+ const TaskParams = z.object({ id: z.string().min(1).max(100), taskId: z.string().min(1).max(100) });
19
+ const OutputQuery = z.object({
20
+ tail: z.coerce.number().int().min(1).max(OUTPUT_TAIL_MAX_BYTES).default(OUTPUT_TAIL_MAX_BYTES),
21
+ });
22
+ class BackgroundConversationParamsDto extends createZodDto(ConversationParams, "BackgroundConversationParams") {
23
+ }
24
+ class BackgroundTaskParamsDto extends createZodDto(TaskParams, "BackgroundTaskParams") {
25
+ }
26
+ class BackgroundOutputQueryDto extends createZodDto(OutputQuery, "BackgroundOutputQuery") {
27
+ }
28
+ /**
29
+ * Background Bash tasks of a conversation (spec 2026-11-05-background-bash-design §5). Visible to
30
+ * whoever sees the conversation, stoppable by whoever may abort its run (the owner): the same
31
+ * checks as `GET :id` and `POST :id/abort`.
32
+ */
33
+ let BackgroundController = class BackgroundController {
34
+ tasks;
35
+ constructor(tasks) {
36
+ this.tasks = tasks;
37
+ }
38
+ /** The conversation's tasks and its sub-agents' (`subagentId`), newest first. */
39
+ async list(principal, org, params) {
40
+ const p = requireUser(principal);
41
+ return { tasks: await this.tasks.list(org.orgId, params.id, p.account.id) };
42
+ }
43
+ /**
44
+ * Stops a task like a person's stop: 200 `{ task }` with the row after the stop. A task that is
45
+ * still `running` with `stopping: true` is being stopped by another request (or is still
46
+ * launching); a stop that fails is an error and the task keeps running.
47
+ */
48
+ async stop(principal, org, params) {
49
+ const p = requireUser(principal);
50
+ return {
51
+ task: await this.tasks.stop(org.orgId, params.id, p.account.id, params.taskId, patTokenId(p)),
52
+ };
53
+ }
54
+ /** The end of the task's output (`tail` bytes, at most 65536); 409 when it cannot be read now. */
55
+ async output(principal, org, params, q) {
56
+ const p = requireUser(principal);
57
+ return this.tasks.output(org.orgId, params.id, p.account.id, params.taskId, q.tail);
58
+ }
59
+ };
60
+ __decorate([
61
+ Get(":orgId/conversations/:id/background-tasks"),
62
+ OrgAction("agents.use"),
63
+ __param(0, CurrentPrincipal()),
64
+ __param(1, CurrentOrg()),
65
+ __param(2, ZParam(BackgroundConversationParamsDto))
66
+ ], BackgroundController.prototype, "list", null);
67
+ __decorate([
68
+ Post(":orgId/conversations/:id/background-tasks/:taskId/stop"),
69
+ HttpCode(200),
70
+ OrgAction("agents.use"),
71
+ __param(0, CurrentPrincipal()),
72
+ __param(1, CurrentOrg()),
73
+ __param(2, ZParam(BackgroundTaskParamsDto))
74
+ ], BackgroundController.prototype, "stop", null);
75
+ __decorate([
76
+ Get(":orgId/conversations/:id/background-tasks/:taskId/output"),
77
+ OrgAction("agents.use"),
78
+ __param(0, CurrentPrincipal()),
79
+ __param(1, CurrentOrg()),
80
+ __param(2, ZParam(BackgroundTaskParamsDto)),
81
+ __param(3, ZQuery(BackgroundOutputQueryDto))
82
+ ], BackgroundController.prototype, "output", null);
83
+ BackgroundController = __decorate([
84
+ Controller("api/v1/orgs"),
85
+ __param(0, Inject(BackgroundTasks))
86
+ ], BackgroundController);
87
+ export { BackgroundController };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Background Bash (spec 2026-11-05-background-bash-design): the task store (from
3
+ * {@link BackgroundStoreModule}, which `SandboxModule` imports too), the finish of a task with its
4
+ * event ({@link BackgroundTaskFinisher}) and the stops ({@link BackgroundTaskStopper}). The wake of
5
+ * an idle conversation (`BackgroundWake`) is the runtime's (`RuntimeModule`): it needs the run
6
+ * queue, and the runtime imports this module. The poll and prune jobs
7
+ * (`BackgroundTaskPollJob`) are the worker role's (`WorkerModule`).
8
+ */
9
+ export declare class BackgroundModule {
10
+ }
@@ -0,0 +1,32 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Module } from "@nestjs/common";
8
+ import { ConversationAccessModule } from "../conversations/access.js";
9
+ import { EventLogModule } from "../conversations/event-log.js";
10
+ import { SandboxModule } from "../sandbox/sandbox.module.js";
11
+ import { BackgroundTaskFinisher } from "./finish.js";
12
+ import { BackgroundTaskStopper } from "./stop.js";
13
+ import { BackgroundStoreModule } from "./store.module.js";
14
+ import { BackgroundTasks } from "./tasks.service.js";
15
+ /**
16
+ * Background Bash (spec 2026-11-05-background-bash-design): the task store (from
17
+ * {@link BackgroundStoreModule}, which `SandboxModule` imports too), the finish of a task with its
18
+ * event ({@link BackgroundTaskFinisher}) and the stops ({@link BackgroundTaskStopper}). The wake of
19
+ * an idle conversation (`BackgroundWake`) is the runtime's (`RuntimeModule`): it needs the run
20
+ * queue, and the runtime imports this module. The poll and prune jobs
21
+ * (`BackgroundTaskPollJob`) are the worker role's (`WorkerModule`).
22
+ */
23
+ let BackgroundModule = class BackgroundModule {
24
+ };
25
+ BackgroundModule = __decorate([
26
+ Module({
27
+ imports: [BackgroundStoreModule, SandboxModule, EventLogModule, ConversationAccessModule],
28
+ providers: [BackgroundTaskFinisher, BackgroundTaskStopper, BackgroundTasks],
29
+ exports: [BackgroundStoreModule, BackgroundTaskFinisher, BackgroundTaskStopper, BackgroundTasks],
30
+ })
31
+ ], BackgroundModule);
32
+ export { BackgroundModule };
@@ -0,0 +1,43 @@
1
+ import { EventLog } from "../conversations/event-log.js";
2
+ import { TransactionHost } from "../infra/database/transaction-host.js";
3
+ import { KernelEvents } from "../infra/events/kernel-events.js";
4
+ import { type BackgroundTaskRow, BackgroundTaskStore } from "./store.js";
5
+ /** Where a task ends (as {@link BackgroundTaskStore.finish} takes it). */
6
+ export type BackgroundFinish = Parameters<BackgroundTaskStore["finish"]>[1];
7
+ /**
8
+ * Who stopped a task, when its finish must never wake the conversation: a person (who is looking at
9
+ * it) or the deletion of the conversation.
10
+ */
11
+ export type BackgroundStoppedBy = "person" | "deletion";
12
+ /** The idempotency key of a task's `background_task_finished` event. */
13
+ export declare const finishedEventKey: (taskId: string) => string;
14
+ /**
15
+ * Takes a task out of `running` (spec 2026-11-05-background-bash-design §4.3): one transaction holds
16
+ * the conditional status update and the `background_task_finished` event of the owning
17
+ * conversation, so a crash between them cannot lose the event, and of two paths finishing the same
18
+ * task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
19
+ * the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
20
+ * that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
21
+ * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
22
+ *
23
+ * Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
24
+ */
25
+ export declare class BackgroundTaskFinisher {
26
+ private readonly store;
27
+ private readonly eventLog;
28
+ private readonly events;
29
+ readonly txHost: TransactionHost;
30
+ constructor(store: Pick<BackgroundTaskStore, "finish">, eventLog: EventLog, events: KernelEvents, txHost: TransactionHost);
31
+ /**
32
+ * Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
33
+ * `reported` when a tool call already told the model in its run. False (nothing written) when the
34
+ * task was no longer running, or, with `opts.unclaimed` (the poller), when a live stop claimed it:
35
+ * the stop's finish is then the only one. `opts.stoppedBy` marks a stop that must never wake the
36
+ * conversation: `"person"` (a person stopped it) or `"deletion"` (its conversation is being
37
+ * deleted).
38
+ */
39
+ finish(task: BackgroundTaskRow, to: BackgroundFinish, lastLines: string, reported: boolean, opts?: {
40
+ unclaimed?: boolean;
41
+ stoppedBy?: BackgroundStoppedBy;
42
+ }): Promise<boolean>;
43
+ }
@@ -0,0 +1,94 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Inject, Injectable } from "@nestjs/common";
11
+ import { EventLog } from "../conversations/event-log.js";
12
+ import { afterCommit } from "../infra/database/after-commit.js";
13
+ import { TransactionHost } from "../infra/database/transaction-host.js";
14
+ import { Transactional } from "../infra/database/transactional.decorator.js";
15
+ import { KernelEvents } from "../infra/events/kernel-events.js";
16
+ import { LAST_LINES_MAX_CHARS } from "./session.js";
17
+ import { BackgroundTaskStore } from "./store.js";
18
+ /** The idempotency key of a task's `background_task_finished` event. */
19
+ export const finishedEventKey = (taskId) => `background_task_finished:${taskId}`;
20
+ /**
21
+ * Takes a task out of `running` (spec 2026-11-05-background-bash-design §4.3): one transaction holds
22
+ * the conditional status update and the `background_task_finished` event of the owning
23
+ * conversation, so a crash between them cannot lose the event, and of two paths finishing the same
24
+ * task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
25
+ * the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
26
+ * that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
27
+ * (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
28
+ *
29
+ * Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
30
+ */
31
+ let BackgroundTaskFinisher = class BackgroundTaskFinisher {
32
+ store;
33
+ eventLog;
34
+ events;
35
+ txHost;
36
+ constructor(store, eventLog, events, txHost) {
37
+ this.store = store;
38
+ this.eventLog = eventLog;
39
+ this.events = events;
40
+ this.txHost = txHost;
41
+ }
42
+ /**
43
+ * Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
44
+ * `reported` when a tool call already told the model in its run. False (nothing written) when the
45
+ * task was no longer running, or, with `opts.unclaimed` (the poller), when a live stop claimed it:
46
+ * the stop's finish is then the only one. `opts.stoppedBy` marks a stop that must never wake the
47
+ * conversation: `"person"` (a person stopped it) or `"deletion"` (its conversation is being
48
+ * deleted).
49
+ */
50
+ async finish(task, to, lastLines, reported, opts = {}) {
51
+ if (!(await this.store.finish(task.id, to, opts)))
52
+ return false;
53
+ const [event] = await this.eventLog.appendIn(task.conversationId, [
54
+ {
55
+ type: "background_task_finished",
56
+ idemKey: finishedEventKey(task.id),
57
+ payload: {
58
+ taskId: task.id,
59
+ description: task.description,
60
+ status: to.status,
61
+ exitCode: to.exitCode ?? null,
62
+ elapsedMs: Math.max(0, Date.now() - task.startedAt.getTime()),
63
+ lastLines: lastLines.length > LAST_LINES_MAX_CHARS
64
+ ? lastLines.slice(-LAST_LINES_MAX_CHARS)
65
+ : lastLines,
66
+ outputPath: `/workspace/${task.outputPath}`,
67
+ reported,
68
+ ...(to.lostReason !== undefined && { lostReason: to.lostReason }),
69
+ ...(opts.stoppedBy !== undefined && { stoppedBy: opts.stoppedBy }),
70
+ },
71
+ },
72
+ ]);
73
+ const conversations = [...new Set([task.conversationId, task.sandboxConversationId])];
74
+ afterCommit(async () => {
75
+ if (event)
76
+ await this.eventLog.notify(task.conversationId, event.seq);
77
+ for (const c of conversations)
78
+ await this.eventLog.publishChanged(c, "background");
79
+ await this.events.emit("background_task.finished", { conversationId: task.conversationId });
80
+ });
81
+ return true;
82
+ }
83
+ };
84
+ __decorate([
85
+ Transactional()
86
+ ], BackgroundTaskFinisher.prototype, "finish", null);
87
+ BackgroundTaskFinisher = __decorate([
88
+ Injectable(),
89
+ __param(0, Inject(BackgroundTaskStore)),
90
+ __param(1, Inject(EventLog)),
91
+ __param(2, Inject(KernelEvents)),
92
+ __param(3, Inject(TransactionHost))
93
+ ], BackgroundTaskFinisher);
94
+ export { BackgroundTaskFinisher };
@@ -0,0 +1,14 @@
1
+ import { TransactionHost } from "../infra/database/transaction-host.js";
2
+ import { XactLock } from "../infra/database/xact-lock.js";
3
+ /**
4
+ * The launch lock of one sandbox (decision R9: one provider per transaction-scoped lock):
5
+ * serializes background task reservations in a sandbox, so the count of running tasks checked
6
+ * against `sandbox.background.maxTasks` and the insert of the new row are one step. Joins the
7
+ * caller's transaction (`mandatory`), which holds it until it ends.
8
+ */
9
+ export declare class BackgroundLaunchLock {
10
+ private readonly xactLock;
11
+ readonly txHost: TransactionHost;
12
+ constructor(xactLock: XactLock, txHost: TransactionHost);
13
+ lock(sandboxConversationId: string): Promise<void>;
14
+ }
@@ -0,0 +1,39 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Inject, Injectable } from "@nestjs/common";
11
+ import { TransactionHost } from "../infra/database/transaction-host.js";
12
+ import { Transactional } from "../infra/database/transactional.decorator.js";
13
+ import { XactLock } from "../infra/database/xact-lock.js";
14
+ /**
15
+ * The launch lock of one sandbox (decision R9: one provider per transaction-scoped lock):
16
+ * serializes background task reservations in a sandbox, so the count of running tasks checked
17
+ * against `sandbox.background.maxTasks` and the insert of the new row are one step. Joins the
18
+ * caller's transaction (`mandatory`), which holds it until it ends.
19
+ */
20
+ let BackgroundLaunchLock = class BackgroundLaunchLock {
21
+ xactLock;
22
+ txHost;
23
+ constructor(xactLock, txHost) {
24
+ this.xactLock = xactLock;
25
+ this.txHost = txHost;
26
+ }
27
+ async lock(sandboxConversationId) {
28
+ await this.xactLock.lock(`capybara.background-launch:${sandboxConversationId}`);
29
+ }
30
+ };
31
+ __decorate([
32
+ Transactional({ propagation: "mandatory" })
33
+ ], BackgroundLaunchLock.prototype, "lock", null);
34
+ BackgroundLaunchLock = __decorate([
35
+ Injectable(),
36
+ __param(0, Inject(XactLock)),
37
+ __param(1, Inject(TransactionHost))
38
+ ], BackgroundLaunchLock);
39
+ export { BackgroundLaunchLock };
@@ -0,0 +1,36 @@
1
+ import type { KernelConfig } from "../config/schema.js";
2
+ /**
3
+ * Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
4
+ * one short exec that starts the command detached from the exec, and the model-visible result.
5
+ * Every path is relative to `/workspace` (the launch exec's `cwd`), so a provider that maps
6
+ * `/workspace` needs no rewriting inside the script.
7
+ */
8
+ /** The launch exec's own time limit: it only writes a file and forks. */
9
+ export declare const LAUNCH_EXEC_TIMEOUT_MS = 30000;
10
+ /**
11
+ * The longest a background task may run, in seconds: `sandbox.background.maxSeconds`, at most
12
+ * `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
13
+ */
14
+ export declare const backgroundMaxSeconds: (cfg: Pick<KernelConfig, "sandbox">) => number;
15
+ /** The directory of the task files, relative to `/workspace`. */
16
+ export declare const TASKS_DIR = ".capybara/tasks";
17
+ /** A task's output file, relative to `/workspace` (`output_path`). */
18
+ export declare const taskOutputPath: (id: string) => string;
19
+ /** The file the launch writes the session id to, relative to `/workspace`. */
20
+ export declare const taskSidPath: (id: string) => string;
21
+ /** A new task id: `bt_` plus 6 lowercase base32 characters. */
22
+ export declare function newTaskId(): string;
23
+ /** A task id ({@link newTaskId}). */
24
+ export declare const TASK_ID_RE: RegExp;
25
+ /**
26
+ * The shell of the launch exec: writes `command` (base64 in the script, so nothing is quoted by
27
+ * hand) to `.capybara/tasks/<id>.sh` and starts it with bash (the shell a foreground `Bash` call
28
+ * runs its command in) in a session of its own under `timeout -s KILL <seconds>`, with its exit
29
+ * status written to `<id>.exit` and its output to `<id>.out`; writes the session id (`$!`, the pid
30
+ * `setsid` started) to `<id>.sid` and prints it. A sandbox without one of the needed programs fails with a readable error on
31
+ * stderr and exit code 127, before anything starts. `id` must be a task id and `seconds` a
32
+ * positive integer (both go into the script unquoted).
33
+ */
34
+ export declare function launchScript(id: string, command: string, seconds: number): string;
35
+ /** The model-visible result of a launch (spec §2.1). */
36
+ export declare function startedText(id: string, description: string): string;
@@ -0,0 +1,70 @@
1
+ import { randomBytes } from "node:crypto";
2
+ /**
3
+ * Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
4
+ * one short exec that starts the command detached from the exec, and the model-visible result.
5
+ * Every path is relative to `/workspace` (the launch exec's `cwd`), so a provider that maps
6
+ * `/workspace` needs no rewriting inside the script.
7
+ */
8
+ /** The launch exec's own time limit: it only writes a file and forks. */
9
+ export const LAUNCH_EXEC_TIMEOUT_MS = 30_000;
10
+ /**
11
+ * The longest a background task may run, in seconds: `sandbox.background.maxSeconds`, at most
12
+ * `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
13
+ */
14
+ export const backgroundMaxSeconds = (cfg) => Math.min(cfg.sandbox.background.maxSeconds, cfg.sandbox.limits.maxCommandSeconds);
15
+ /** The directory of the task files, relative to `/workspace`. */
16
+ export const TASKS_DIR = ".capybara/tasks";
17
+ /** A task's output file, relative to `/workspace` (`output_path`). */
18
+ export const taskOutputPath = (id) => `${TASKS_DIR}/${id}.out`;
19
+ /** The file the launch writes the session id to, relative to `/workspace`. */
20
+ export const taskSidPath = (id) => `${TASKS_DIR}/${id}.sid`;
21
+ const BASE32 = "abcdefghijklmnopqrstuvwxyz234567";
22
+ /** A new task id: `bt_` plus 6 lowercase base32 characters. */
23
+ export function newTaskId() {
24
+ const bytes = randomBytes(6);
25
+ let out = "bt_";
26
+ for (const b of bytes)
27
+ out += BASE32[b & 31];
28
+ return out;
29
+ }
30
+ /** The programs the launch and the later polls and stops need inside the sandbox. */
31
+ const REQUIRED = ["bash", "setsid", "nohup", "timeout", "pgrep", "pkill", "base64"];
32
+ /** A task id ({@link newTaskId}). */
33
+ export const TASK_ID_RE = /^bt_[a-z2-7]{6}$/;
34
+ /**
35
+ * The shell of the launch exec: writes `command` (base64 in the script, so nothing is quoted by
36
+ * hand) to `.capybara/tasks/<id>.sh` and starts it with bash (the shell a foreground `Bash` call
37
+ * runs its command in) in a session of its own under `timeout -s KILL <seconds>`, with its exit
38
+ * status written to `<id>.exit` and its output to `<id>.out`; writes the session id (`$!`, the pid
39
+ * `setsid` started) to `<id>.sid` and prints it. A sandbox without one of the needed programs fails with a readable error on
40
+ * stderr and exit code 127, before anything starts. `id` must be a task id and `seconds` a
41
+ * positive integer (both go into the script unquoted).
42
+ */
43
+ export function launchScript(id, command, seconds) {
44
+ if (!TASK_ID_RE.test(id))
45
+ throw new Error(`invalid background task id: ${JSON.stringify(id)}`);
46
+ if (!Number.isSafeInteger(seconds) || seconds < 1) {
47
+ throw new Error(`invalid background task seconds: ${seconds}`);
48
+ }
49
+ const b64 = Buffer.from(command, "utf8").toString("base64");
50
+ const t = `${TASKS_DIR}/${id}`;
51
+ return [
52
+ "set -e",
53
+ `for c in ${REQUIRED.join(" ")}; do command -v "$c" >/dev/null 2>&1 || { echo "Background tasks need ${REQUIRED.join(", ")} in the sandbox; $c is missing." >&2; exit 127; }; done`,
54
+ `mkdir -p ${TASKS_DIR}`,
55
+ `printf '%s' '${b64}' | base64 -d > ${t}.sh`,
56
+ `setsid nohup sh -c 'timeout -s KILL ${seconds} bash ${t}.sh; echo $? > ${t}.exit' > ${t}.out 2>&1 < /dev/null &`,
57
+ // the session id goes to a file first: a launch that ends before its output comes back
58
+ // (aborted, timed out) still leaves the id of what it started
59
+ `echo $! > ${t}.sid`,
60
+ `cat ${t}.sid`,
61
+ ].join("\n");
62
+ }
63
+ /** The model-visible result of a launch (spec §2.1). */
64
+ export function startedText(id, description) {
65
+ return [
66
+ `Started background task ${id} ("${description}").`,
67
+ `Output: /workspace/${taskOutputPath(id)}`,
68
+ "Read it with TaskOutput (or Read), stop it with TaskStop. You will be told when it finishes.",
69
+ ].join("\n");
70
+ }