@capybara-hq/kernel 0.1.0-rc.3 → 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 (226) 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 +10 -0
  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 +30 -7
  14. package/dist/agents/definition.d.ts +4 -0
  15. package/dist/agents/definition.js +10 -0
  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 +1 -1
  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/claude/agents.js +2 -0
  56. package/dist/catalog/marketplaces.service.d.ts +1 -1
  57. package/dist/catalog/plugin-import.js +1 -1
  58. package/dist/catalog/plugin-upgrade.js +1 -1
  59. package/dist/channels/cards.js +1 -1
  60. package/dist/channels/connections.service.d.ts +16 -1
  61. package/dist/channels/connections.service.js +38 -4
  62. package/dist/channels/threads.d.ts +5 -0
  63. package/dist/channels/threads.js +8 -0
  64. package/dist/config/schema.d.ts +18 -0
  65. package/dist/config/schema.js +102 -0
  66. package/dist/conversations/access.js +2 -0
  67. package/dist/conversations/attachments.service.d.ts +30 -0
  68. package/dist/conversations/attachments.service.js +130 -5
  69. package/dist/conversations/conversations.controller.d.ts +36 -5
  70. package/dist/conversations/conversations.controller.js +62 -12
  71. package/dist/conversations/conversations.module.js +3 -0
  72. package/dist/conversations/conversations.service.d.ts +44 -2
  73. package/dist/conversations/conversations.service.js +140 -12
  74. package/dist/conversations/event-log.d.ts +1 -1
  75. package/dist/conversations/event-log.js +10 -1
  76. package/dist/conversations/events.d.ts +58 -0
  77. package/dist/conversations/events.js +57 -2
  78. package/dist/conversations/queue.d.ts +3 -1
  79. package/dist/conversations/queue.js +13 -2
  80. package/dist/conversations/queue.service.js +3 -1
  81. package/dist/conversations/titles.d.ts +7 -0
  82. package/dist/conversations/titles.js +15 -0
  83. package/dist/db/kernel-schema.d.ts +58 -2
  84. package/dist/db/pool.d.ts +5 -0
  85. package/dist/db/pool.js +3 -0
  86. package/dist/errors.d.ts +8 -0
  87. package/dist/errors.js +8 -0
  88. package/dist/evals/run-replay.d.ts +5 -2
  89. package/dist/evals/run-replay.js +30 -7
  90. package/dist/evals/step-replay.js +12 -6
  91. package/dist/evals/trajectory.js +1 -1
  92. package/dist/extensions/admin-ops.js +2 -0
  93. package/dist/extensions/admin-view.d.ts +3 -1
  94. package/dist/extensions/admin-view.js +10 -2
  95. package/dist/extensions/blobs.js +1 -0
  96. package/dist/extensions/config-issues.d.ts +13 -1
  97. package/dist/extensions/config-issues.js +22 -5
  98. package/dist/extensions/config-json-schema.d.ts +43 -7
  99. package/dist/extensions/config-json-schema.js +124 -58
  100. package/dist/extensions/deployment-config-store.d.ts +6 -0
  101. package/dist/extensions/deployment-config.d.ts +50 -3
  102. package/dist/extensions/deployment-config.js +125 -28
  103. package/dist/extensions/display-name.d.ts +22 -0
  104. package/dist/extensions/display-name.js +77 -0
  105. package/dist/extensions/extensions.module.js +6 -0
  106. package/dist/extensions/host.d.ts +26 -1
  107. package/dist/extensions/host.js +82 -9
  108. package/dist/extensions/org-config.controller.d.ts +11 -3
  109. package/dist/extensions/org-config.controller.js +14 -6
  110. package/dist/extensions/org-config.d.ts +6 -0
  111. package/dist/extensions/org-config.js +12 -13
  112. package/dist/extensions/package.d.ts +14 -1
  113. package/dist/extensions/package.js +51 -2
  114. package/dist/extensions/permission-view.d.ts +3 -0
  115. package/dist/extensions/point-defaults.controller.d.ts +34 -0
  116. package/dist/extensions/point-defaults.controller.js +71 -0
  117. package/dist/extensions/point-defaults.d.ts +64 -0
  118. package/dist/extensions/point-defaults.js +206 -0
  119. package/dist/extensions/registry.d.ts +1 -1
  120. package/dist/extensions/registry.js +2 -0
  121. package/dist/http/api.module.js +8 -0
  122. package/dist/http/nest-logger.d.ts +6 -5
  123. package/dist/http/nest-logger.js +24 -10
  124. package/dist/identity/auth.controller.js +9 -5
  125. package/dist/identity/cookies.d.ts +20 -5
  126. package/dist/identity/cookies.js +44 -6
  127. package/dist/identity/me.controller.d.ts +5 -1
  128. package/dist/identity/me.controller.js +12 -3
  129. package/dist/identity/sessions.d.ts +12 -0
  130. package/dist/identity/sessions.js +45 -0
  131. package/dist/index.d.ts +1 -1
  132. package/dist/infra/config/kernel-config.service.js +6 -0
  133. package/dist/infra/database/database.module.js +1 -0
  134. package/dist/infra/events/kernel-events.d.ts +8 -0
  135. package/dist/infra/lifecycle/process-guards.js +4 -0
  136. package/dist/infra/logging/logging.module.d.ts +4 -2
  137. package/dist/infra/logging/logging.module.js +24 -6
  138. package/dist/infra/queue/redis-connection.d.ts +6 -0
  139. package/dist/infra/queue/redis-connection.js +7 -0
  140. package/dist/infra/redis/redis.module.js +6 -2
  141. package/dist/infra/tokens.d.ts +6 -1
  142. package/dist/infra/tokens.js +6 -1
  143. package/dist/log.d.ts +13 -1
  144. package/dist/log.js +93 -17
  145. package/dist/models/purposes.d.ts +23 -0
  146. package/dist/models/purposes.js +31 -10
  147. package/dist/orgs/offboarding.d.ts +3 -1
  148. package/dist/orgs/offboarding.js +26 -16
  149. package/dist/orgs/settings.service.d.ts +5 -0
  150. package/dist/orgs/settings.service.js +13 -2
  151. package/dist/roles/worker.module.js +6 -0
  152. package/dist/runtime/context/build.d.ts +9 -7
  153. package/dist/runtime/context/build.js +27 -19
  154. package/dist/runtime/context/claude-tools.d.ts +2 -1
  155. package/dist/runtime/context/claude-tools.js +5 -4
  156. package/dist/runtime/context/compaction.js +4 -0
  157. package/dist/runtime/langgraph-loop.js +3 -2
  158. package/dist/runtime/loop.d.ts +4 -3
  159. package/dist/runtime/maintenance.d.ts +4 -2
  160. package/dist/runtime/maintenance.js +17 -5
  161. package/dist/runtime/model-call.d.ts +17 -1
  162. package/dist/runtime/model-call.js +3 -2
  163. package/dist/runtime/providers.d.ts +6 -0
  164. package/dist/runtime/providers.js +8 -0
  165. package/dist/runtime/run-queue.d.ts +25 -2
  166. package/dist/runtime/run-queue.js +129 -33
  167. package/dist/runtime/run-worker.js +16 -3
  168. package/dist/runtime/runtime.module.js +8 -3
  169. package/dist/runtime/step-log.d.ts +6 -0
  170. package/dist/runtime/step-log.js +16 -2
  171. package/dist/runtime/subagents/definition.js +3 -1
  172. package/dist/runtime/usage-report.d.ts +2 -1
  173. package/dist/runtime/usage-report.js +2 -1
  174. package/dist/runtime/waits.d.ts +8 -2
  175. package/dist/runtime/waits.js +37 -11
  176. package/dist/sandbox/sandbox.module.js +6 -1
  177. package/dist/sandbox/sandbox.service.d.ts +61 -3
  178. package/dist/sandbox/sandbox.service.js +141 -7
  179. package/dist/tools/approvals.service.js +2 -0
  180. package/dist/tools/builtin-names.d.ts +20 -2
  181. package/dist/tools/builtin-names.js +29 -2
  182. package/dist/tools/builtins/builtin-tools.d.ts +15 -1
  183. package/dist/tools/builtins/builtin-tools.js +34 -2
  184. package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
  185. package/dist/tools/builtins/sandbox/bash.js +230 -3
  186. package/dist/tools/builtins/sandbox/common.d.ts +27 -4
  187. package/dist/tools/builtins/sandbox/common.js +7 -3
  188. package/dist/tools/builtins/sandbox/deliver.js +1 -1
  189. package/dist/tools/builtins/sandbox/index.d.ts +2 -0
  190. package/dist/tools/builtins/sandbox/index.js +6 -0
  191. package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
  192. package/dist/tools/builtins/sandbox/task-output.js +304 -0
  193. package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
  194. package/dist/tools/builtins/sandbox/task-stop.js +112 -0
  195. package/dist/tools/builtins/web/errors.d.ts +8 -0
  196. package/dist/tools/builtins/web/errors.js +53 -0
  197. package/dist/tools/builtins/web/extract.d.ts +62 -0
  198. package/dist/tools/builtins/web/extract.js +267 -0
  199. package/dist/tools/builtins/web/fetch.d.ts +55 -0
  200. package/dist/tools/builtins/web/fetch.js +174 -0
  201. package/dist/tools/builtins/web/index.d.ts +13 -0
  202. package/dist/tools/builtins/web/index.js +12 -0
  203. package/dist/tools/builtins/web/page-cache.d.ts +25 -0
  204. package/dist/tools/builtins/web/page-cache.js +77 -0
  205. package/dist/tools/builtins/web/search.d.ts +25 -0
  206. package/dist/tools/builtins/web/search.js +209 -0
  207. package/dist/tools/builtins/web/url.d.ts +17 -0
  208. package/dist/tools/builtins/web/url.js +43 -0
  209. package/dist/tools/oauth/flow-paths.d.ts +1 -1
  210. package/dist/tools/oauth/flow-paths.js +1 -1
  211. package/dist/tools/pipeline.js +15 -0
  212. package/dist/tools/registry.d.ts +22 -3
  213. package/dist/tools/registry.js +26 -11
  214. package/dist/tools/tools.module.js +12 -0
  215. package/dist/tools/validate.js +1 -1
  216. package/package.json +3 -3
  217. package/src/migrations/0064_account_last_org.sql +7 -0
  218. package/src/migrations/0065_conversation_archive.sql +8 -0
  219. package/src/migrations/0066_extension_display_name.sql +4 -0
  220. package/src/migrations/0067_agent_profiles.sql +20 -0
  221. package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
  222. package/src/migrations/0069_attachments_agent_idx.sql +3 -0
  223. package/src/migrations/0070_background_tasks.sql +37 -0
  224. package/src/migrations/0071_background_tasks_reserve.sql +6 -0
  225. package/src/migrations/0072_background_tasks_stopping.sql +5 -0
  226. package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
@@ -0,0 +1,225 @@
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 { KernelDb } from "../infra/database/kernel-db.js";
12
+ import { KERNEL_LOGGER } from "../infra/tokens.js";
13
+ import { SandboxService } from "../sandbox/sandbox.service.js";
14
+ import { BackgroundTaskFinisher, } from "./finish.js";
15
+ import { TASKS_DIR } from "./launch.js";
16
+ import { assertTaskRef, decodeTail, killSession, tailCommand } from "./session.js";
17
+ import { BackgroundTaskStore } from "./store.js";
18
+ /** How long a stop waits after SIGTERM before SIGKILL (spec §2.3). */
19
+ export const STOP_GRACE_MS = 5000;
20
+ /** The time limit of the SIGTERM exec: the grace period plus some room. */
21
+ const TERM_EXEC_TIMEOUT_MS = STOP_GRACE_MS + 5000;
22
+ /** The time limit of reading the last lines after a stop. */
23
+ const TAIL_EXEC_TIMEOUT_MS = 10_000;
24
+ /**
25
+ * Exit code 0 with `exited <code>` when the task had already finished (its exit file exists),
26
+ * else sends SIGTERM to the session and waits up to {@link STOP_GRACE_MS} for it to empty, then
27
+ * prints `term`. Relative to `/workspace`.
28
+ */
29
+ function termScript(id, sid) {
30
+ const f = `${TASKS_DIR}/${id}`;
31
+ const tries = Math.ceil(STOP_GRACE_MS / 200);
32
+ return [
33
+ `if [ -s ${f}.exit ]; then echo "exited $(cat ${f}.exit)"; exit 0; fi`,
34
+ `pkill -TERM -s ${sid} >/dev/null 2>&1`,
35
+ `i=0; while [ "$i" -lt ${tries} ] && pgrep -s ${sid} >/dev/null 2>&1; do sleep 0.2; i=$((i+1)); done`,
36
+ "echo term",
37
+ ].join("\n");
38
+ }
39
+ /** How a task that exited by itself ends (spec §4.3): exit 137 past its deadline timed out. */
40
+ export function exitedAs(task, code, now) {
41
+ if (code === 0)
42
+ return { status: "completed", exitCode: 0 };
43
+ if (code === 137 && now.getTime() >= task.deadlineAt.getTime()) {
44
+ return { status: "timed_out", exitCode: 137 };
45
+ }
46
+ return { status: "failed", exitCode: code };
47
+ }
48
+ /** How a task whose sandbox is not running ends (`ifRunning`'s `not_running` status). */
49
+ export function notRunningAs(status, killPending) {
50
+ return {
51
+ status: "lost",
52
+ lostReason: status === "suspended" ? "sandbox_suspended" : "sandbox_gone",
53
+ killPending,
54
+ };
55
+ }
56
+ /** A task found in another sandbox instance (a rebuild): its processes are gone with the old one. */
57
+ export const RECREATED = {
58
+ status: "lost",
59
+ lostReason: "sandbox_recreated",
60
+ killPending: false,
61
+ };
62
+ /**
63
+ * Stops background tasks (spec 2026-11-05-background-bash-design §2.3, §4.3): SIGTERM to the
64
+ * task's session, up to 5 s for it to empty, then SIGKILL until it is empty, all through
65
+ * `SandboxService.ifRunning` (it never creates or resumes a sandbox); the task then finishes as
66
+ * `stopped` with its last 20 lines. A task that had already exited finishes as its exit says. When
67
+ * the sandbox is not running the task finishes as `lost` with `kill_pending` (killed right after a
68
+ * resume); in a rebuilt sandbox as `lost` (`sandbox_recreated`). Processes a task that had
69
+ * already exited left running are killed as well.
70
+ */
71
+ let BackgroundTaskStopper = class BackgroundTaskStopper {
72
+ store;
73
+ finisher;
74
+ sandbox;
75
+ db;
76
+ log;
77
+ constructor(store, finisher, sandbox, db, log) {
78
+ this.store = store;
79
+ this.finisher = finisher;
80
+ this.sandbox = sandbox;
81
+ this.db = db;
82
+ this.log = log;
83
+ }
84
+ /**
85
+ * Stops `task` and returns its row afterwards. A task that is no longer running, or that another
86
+ * live stop already claimed, is returned as it is now.
87
+ *
88
+ * The stop first claims the task (`stopping_at`), so the poller leaves it alone while the
89
+ * signals take effect and the stop's finish is its only one. A reservation whose launch has not
90
+ * started the command yet (no `sid`) is only claimed: the launch then kills what it starts and
91
+ * finishes the row `stopped`. When the stop fails while the sandbox runs (busy, a provider
92
+ * error, a session that could not be confirmed empty), the claim is given up, the task stays
93
+ * `running` and the error is thrown.
94
+ */
95
+ async stop(task, why, signal) {
96
+ if (!(await this.store.claimStop(task.id)))
97
+ return (await this.store.get(task.id)) ?? task;
98
+ // read after the claim: a launch recording its session before the claim is seen here, one
99
+ // recording it after sees the claim (both are single-row updates of the same row)
100
+ const cur = (await this.store.get(task.id)) ?? task;
101
+ if (cur.status !== "running" || cur.sid === null || cur.sandboxCreatedAt === null)
102
+ return cur;
103
+ const sid = cur.sid;
104
+ assertTaskRef(cur.id, sid);
105
+ const reported = why === "tool";
106
+ // a person's stop never wakes the conversation (background Bash §3, fix round 1), nor does
107
+ // the stop of a conversation being deleted
108
+ const stoppedBy = why === "person" ? "person" : why === "conversation_deleted" ? "deletion" : undefined;
109
+ const opts = stoppedBy === undefined ? {} : { stoppedBy };
110
+ let r;
111
+ try {
112
+ r = await this.sandbox.ifRunning(cur.sandboxConversationId, { createdAt: cur.sandboxCreatedAt }, async (p, h, sig) => {
113
+ const term = await p.exec(h, {
114
+ command: termScript(cur.id, sid),
115
+ cwd: "/workspace",
116
+ timeoutMs: TERM_EXEC_TIMEOUT_MS,
117
+ maxOutputBytes: 4096,
118
+ signal: sig,
119
+ });
120
+ const exited = /^exited (-?[0-9]+)$/.exec(term.stdout.trim());
121
+ if (term.exitCode !== 0 || (!exited && term.stdout.trim() !== "term")) {
122
+ throw new Error(`the stop of background task ${cur.id} failed (${term.exitCode})`);
123
+ }
124
+ // also after an exit: processes the command left running are stopped with the task
125
+ if (!(await killSession(p, h, sid))) {
126
+ throw new Error(`background task ${cur.id} could not be confirmed stopped`);
127
+ }
128
+ const tail = await p.exec(h, {
129
+ command: tailCommand(cur.id),
130
+ cwd: "/workspace",
131
+ timeoutMs: TAIL_EXEC_TIMEOUT_MS,
132
+ maxOutputBytes: 64 * 1024,
133
+ signal: sig,
134
+ });
135
+ const lastLines = tail.exitCode === 0 ? decodeTail(tail.stdout) : "";
136
+ // a task that had exited before the signal ends as its exit says
137
+ const to = exited
138
+ ? exitedAs(cur, Number(exited[1]), new Date())
139
+ : { status: "stopped" };
140
+ return { to, lastLines };
141
+ }, signal);
142
+ }
143
+ catch (err) {
144
+ await this.store
145
+ .releaseStop(cur.id)
146
+ .catch((e) => this.log.warn("cannot release a background task stop", { taskId: cur.id, err: e }));
147
+ throw err;
148
+ }
149
+ if (r.kind === "ran") {
150
+ await this.finisher.finish(cur, r.value.to, r.value.lastLines, reported, opts);
151
+ }
152
+ else if (r.kind === "not_running") {
153
+ await this.finisher.finish(cur, notRunningAs(r.status, true), "", reported, opts);
154
+ }
155
+ else {
156
+ await this.finisher.finish(cur, RECREATED, "", reported, opts);
157
+ }
158
+ return (await this.store.get(cur.id)) ?? cur;
159
+ }
160
+ /**
161
+ * After the sub-agent run `runId` ended (spec §4.3): stops the running tasks that run started in
162
+ * `conversationId` when it is a sub-agent's conversation (nobody can read or stop them through
163
+ * that run's model after that); any other conversation is left alone. Only that run's tasks: a
164
+ * follow-up run of the same child keeps its own when an older run's end is handled late. Never
165
+ * throws (see {@link stopAll}).
166
+ */
167
+ async stopSubagentTasks(conversationId, runId) {
168
+ try {
169
+ const conv = await this.db
170
+ .selectFrom("conversations")
171
+ .select("kind")
172
+ .where("id", "=", conversationId)
173
+ .executeTakeFirst();
174
+ if (conv?.kind !== "subagent")
175
+ return;
176
+ const tasks = (await this.store.runningOwnedBy(conversationId)).filter((t) => t.runId === runId);
177
+ await this.stopAll(tasks, "subagent_end");
178
+ }
179
+ catch (err) {
180
+ this.log.warn("cannot stop the background tasks of a finished sub-agent run", {
181
+ conversationId,
182
+ runId,
183
+ err,
184
+ });
185
+ }
186
+ }
187
+ /**
188
+ * Before a conversation is deleted: stops every running task in its sandbox (its own and its
189
+ * sub-agents'). Never throws (see {@link stopAll}).
190
+ */
191
+ async stopForDeletedConversation(conversationId) {
192
+ try {
193
+ await this.stopAll(await this.store.runningIn(conversationId), "conversation_deleted");
194
+ }
195
+ catch (err) {
196
+ this.log.warn("cannot stop the background tasks of a deleted conversation", {
197
+ conversationId,
198
+ err,
199
+ });
200
+ }
201
+ }
202
+ /**
203
+ * Stops `tasks` side by side. A stop that fails while the sandbox runs leaves its task running
204
+ * (logged): the poller still checks it and it ends at its own deadline at the latest.
205
+ */
206
+ async stopAll(tasks, why) {
207
+ await Promise.all(tasks.map(async (t) => {
208
+ try {
209
+ await this.stop(t, why);
210
+ }
211
+ catch (err) {
212
+ this.log.warn("cannot stop a background task; it keeps running", { taskId: t.id, err });
213
+ }
214
+ }));
215
+ }
216
+ };
217
+ BackgroundTaskStopper = __decorate([
218
+ Injectable(),
219
+ __param(0, Inject(BackgroundTaskStore)),
220
+ __param(1, Inject(BackgroundTaskFinisher)),
221
+ __param(2, Inject(SandboxService)),
222
+ __param(3, Inject(KernelDb)),
223
+ __param(4, Inject(KERNEL_LOGGER))
224
+ ], BackgroundTaskStopper);
225
+ export { BackgroundTaskStopper };
@@ -0,0 +1,150 @@
1
+ import { KernelDb } from "../infra/database/kernel-db.js";
2
+ import { TransactionHost } from "../infra/database/transaction-host.js";
3
+ import { BackgroundLaunchLock } from "./launch-lock.js";
4
+ export type BackgroundTaskStatus = "running" | "completed" | "failed" | "stopped" | "timed_out" | "lost";
5
+ /** A task as the kernel reads it: the columns of `capybara.background_tasks`, camel-cased. */
6
+ export interface BackgroundTaskRow {
7
+ id: string;
8
+ orgId: string;
9
+ /** The owner: the child conversation for a sub-agent. */
10
+ conversationId: string;
11
+ /** The root conversation that owns the sandbox. */
12
+ sandboxConversationId: string;
13
+ runId: string;
14
+ callId: string;
15
+ triggerAccountId: string;
16
+ description: string;
17
+ command: string;
18
+ /**
19
+ * The session id (the pid `setsid` started). Null while the launch is reserved but has not
20
+ * started: readers that act on the process (the poller, stops) skip such a row.
21
+ */
22
+ sid: string | null;
23
+ /** Relative to `/workspace`. */
24
+ outputPath: string;
25
+ status: BackgroundTaskStatus;
26
+ lostReason: string | null;
27
+ exitCode: number | null;
28
+ startedAt: Date;
29
+ finishedAt: Date | null;
30
+ deadlineAt: Date;
31
+ /** The sandbox instance the task started in; null while reserved, like `sid`. */
32
+ sandboxCreatedAt: Date | null;
33
+ /** Marked lost while its process may still exist. */
34
+ killPending: boolean;
35
+ /** The bytes `TaskOutput` has already returned. */
36
+ readOffset: number;
37
+ /** When a stop claimed the task ({@link BackgroundTaskStore.claimStop}); null otherwise. */
38
+ stoppingAt: Date | null;
39
+ }
40
+ export type BackgroundTaskInsert = Omit<BackgroundTaskRow, "status" | "finishedAt" | "exitCode" | "lostReason" | "killPending" | "readOffset" | "stoppingAt">;
41
+ /** A launch's reservation: the row before the command started (no `sid`, no instance yet). */
42
+ export type BackgroundTaskReservation = Omit<BackgroundTaskInsert, "sid" | "sandboxCreatedAt">;
43
+ /**
44
+ * How long a stop's claim holds: a claim older than this is abandoned (its stopper died), so the
45
+ * poller checks the task again and another stop may claim it. Longer than any stop takes (the
46
+ * lease wait, the SIGTERM grace, the kill and the tail).
47
+ */
48
+ export declare const STOP_CLAIM_TTL_MS: number;
49
+ /**
50
+ * Background Bash tasks (spec §4.1): one `capybara.background_tasks` row per command started with
51
+ * `run_in_background`. The store only reads and writes rows; the launch, the poller and the tools
52
+ * that drive them are other providers.
53
+ *
54
+ * A launch first reserves its row ({@link BackgroundTaskStore.reserve}: `running`, `sid` null),
55
+ * then starts the command and sets `sid` ({@link BackgroundTaskStore.markStarted}), or finishes
56
+ * the row as `failed` ({@link BackgroundTaskStore.failLaunch}). A running row with a null `sid`
57
+ * counts against `maxTasks` but has no process to check or stop: the poller and stops must skip
58
+ * it (the launch that reserved it settles it; one left over by a launch that died is failed by the
59
+ * poller, {@link BackgroundTaskStore.failAbandonedLaunches}).
60
+ */
61
+ export declare class BackgroundTaskStore {
62
+ private readonly db;
63
+ readonly txHost: TransactionHost;
64
+ private readonly launchLock;
65
+ constructor(db: KernelDb, txHost: TransactionHost, launchLock: BackgroundLaunchLock);
66
+ /**
67
+ * Reserves a launch: under the sandbox's launch lock, counts the running tasks of
68
+ * `row.sandboxConversationId` and inserts `row` as `running` (no `sid`) when fewer than
69
+ * `maxTasks` run. False (nothing written) when the sandbox already runs `maxTasks`.
70
+ */
71
+ reserve(row: BackgroundTaskReservation, maxTasks: number): Promise<boolean>;
72
+ /**
73
+ * Records the started command of a reserved row: `started`, or `stopping` when a stop claimed the
74
+ * reservation meanwhile (the launch then kills the command and finishes the row with
75
+ * {@link finishStoppedLaunch}); false when the row is no longer running.
76
+ */
77
+ markStarted(id: string, to: {
78
+ sid: string;
79
+ sandboxCreatedAt: Date;
80
+ }): Promise<"started" | "stopping" | false>;
81
+ /**
82
+ * Finishes a launch whose reservation a stop claimed, once the launch killed what it started:
83
+ * `stopped`. No `background_task_finished` event: the launching call reports it itself.
84
+ */
85
+ finishStoppedLaunch(id: string): Promise<void>;
86
+ /**
87
+ * Claims a running task for a stop (`stopping_at`): false when it is no longer running or another
88
+ * live stop claimed it ({@link STOP_CLAIM_TTL_MS}). While claimed the poller leaves it alone.
89
+ */
90
+ claimStop(id: string): Promise<boolean>;
91
+ /** Gives up a stop's claim (the stop failed while the sandbox runs: the task keeps running). */
92
+ releaseStop(id: string): Promise<void>;
93
+ /**
94
+ * Finishes a reserved row whose launch failed as `failed`, with `reason` in `lost_reason` (and a
95
+ * stop's claim cleared); a row that already has its sid (the command started and is tracked) is
96
+ * left as it is. No `background_task_finished` event: the launching call reports the failure
97
+ * itself.
98
+ */
99
+ failLaunch(id: string, reason: string): Promise<void>;
100
+ /**
101
+ * Finishes as `failed` (`lost_reason` `launch_abandoned`) every reservation that started before
102
+ * `before`, never got its sid and is not claimed by a live stop: the launch that reserved it died
103
+ * (a crashed replica). A stop's claim older than {@link STOP_CLAIM_TTL_MS} does not hold it: a
104
+ * stop only claims a reservation and leaves the rest to the launch, so a dead launch would keep
105
+ * it (and its slot of `maxTasks`) forever. Conditional on `sid` still null, so a launch
106
+ * completing at that moment wins. No `background_task_finished` event: the command never
107
+ * started and the model never saw its id. Returns the rows it finished (their owner and sandbox
108
+ * conversations, for the caller's `changed: "background"` frames).
109
+ */
110
+ failAbandonedLaunches(before: Date): Promise<{
111
+ id: string;
112
+ conversationId: string;
113
+ sandboxConversationId: string;
114
+ }[]>;
115
+ insert(row: BackgroundTaskInsert): Promise<void>;
116
+ get(id: string): Promise<BackgroundTaskRow | undefined>;
117
+ /** The running tasks of one sandbox, oldest first. */
118
+ runningIn(sandboxConversationId: string): Promise<BackgroundTaskRow[]>;
119
+ /**
120
+ * Every running task that has started (its `sid` is set) and no live stop claimed, across
121
+ * sandboxes, oldest first: what the poller checks. A reservation still launching (`sid` null) is
122
+ * left to its launch, a claimed task to its stop.
123
+ */
124
+ runningStarted(): Promise<BackgroundTaskRow[]>;
125
+ /** The running tasks one conversation owns (not those of its sub-agents), oldest first. */
126
+ runningOwnedBy(conversationId: string): Promise<BackgroundTaskRow[]>;
127
+ countRunning(sandboxConversationId: string): Promise<number>;
128
+ /** The tasks owned by any of the conversations, newest first. */
129
+ listFor(conversationIds: string[]): Promise<BackgroundTaskRow[]>;
130
+ /**
131
+ * Conditional `running` to a final status; false when the row was no longer running (another
132
+ * path finished it first), or, with `opts.unclaimed` (the poller), when a live stop claimed it.
133
+ * The caller's transaction also appends the finished event.
134
+ */
135
+ finish(id: string, to: {
136
+ status: Exclude<BackgroundTaskStatus, "running">;
137
+ exitCode?: number | null;
138
+ lostReason?: string;
139
+ killPending?: boolean;
140
+ }, opts?: {
141
+ unclaimed?: boolean;
142
+ }): Promise<boolean>;
143
+ /** Moves the read offset forward only (a concurrent reader never moves it back). */
144
+ advanceOffset(id: string, to: number): Promise<void>;
145
+ /** Finished tasks of a sandbox whose process may still exist. */
146
+ pendingKills(sandboxConversationId: string): Promise<BackgroundTaskRow[]>;
147
+ clearKill(ids: string[]): Promise<void>;
148
+ /** Deletes finished tasks that ended before `before`; a running one is never deleted. */
149
+ pruneFinished(before: Date): Promise<number>;
150
+ }