@capybara-hq/kernel 0.1.0-rc.23 → 0.1.0-rc.25

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 (293) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.module.d.ts +2 -1
  3. package/dist/admin/admin.module.js +9 -3
  4. package/dist/admin/health.service.d.ts +2 -2
  5. package/dist/admin/health.service.js +6 -2
  6. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  7. package/dist/admin/outbox-dead.controller.js +62 -0
  8. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  9. package/dist/admin/outbox-dead.schemas.js +18 -0
  10. package/dist/admin/outbox-dead.service.d.ts +66 -0
  11. package/dist/admin/outbox-dead.service.js +179 -0
  12. package/dist/admin/overview.service.d.ts +40 -5
  13. package/dist/admin/overview.service.js +41 -7
  14. package/dist/agents/agents.module.js +3 -0
  15. package/dist/agents/agents.service.d.ts +6 -1
  16. package/dist/agents/agents.service.js +11 -2
  17. package/dist/agents/from-template.js +1 -1
  18. package/dist/background/finish.d.ts +5 -2
  19. package/dist/background/finish.js +14 -3
  20. package/dist/background/launch.d.ts +2 -1
  21. package/dist/background/launch.js +5 -2
  22. package/dist/background/store.d.ts +8 -0
  23. package/dist/background/store.js +24 -0
  24. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  25. package/dist/catalog/admin-catalog.controller.js +45 -6
  26. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  27. package/dist/catalog/admin-catalog.schemas.js +8 -0
  28. package/dist/catalog/catalog-import.module.js +0 -4
  29. package/dist/catalog/catalog-scope.service.d.ts +63 -26
  30. package/dist/catalog/catalog-scope.service.js +146 -71
  31. package/dist/catalog/catalog.module.d.ts +2 -1
  32. package/dist/catalog/catalog.module.js +9 -4
  33. package/dist/catalog/catalog.service.d.ts +11 -4
  34. package/dist/catalog/catalog.service.js +21 -5
  35. package/dist/catalog/cli.d.ts +4 -3
  36. package/dist/catalog/cli.js +8 -13
  37. package/dist/catalog/job-ids.d.ts +8 -20
  38. package/dist/catalog/job-ids.js +11 -16
  39. package/dist/catalog/marketplaces.service.d.ts +1 -1
  40. package/dist/catalog/org-plugin-uploads.d.ts +11 -4
  41. package/dist/catalog/org-plugin-uploads.js +21 -6
  42. package/dist/catalog/owned-items.d.ts +6 -13
  43. package/dist/catalog/owned-items.js +11 -29
  44. package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
  45. package/dist/catalog/owned-plugin-cleanup.js +12 -66
  46. package/dist/catalog/platform-components.d.ts +56 -34
  47. package/dist/catalog/platform-components.js +68 -78
  48. package/dist/catalog/platform-import-update.d.ts +50 -0
  49. package/dist/catalog/platform-import-update.js +116 -0
  50. package/dist/catalog/plugin-import.d.ts +64 -26
  51. package/dist/catalog/plugin-import.js +113 -77
  52. package/dist/catalog/plugin-upgrade.d.ts +22 -4
  53. package/dist/catalog/plugin-upgrade.js +26 -19
  54. package/dist/catalog/scope-jobs.d.ts +59 -0
  55. package/dist/catalog/scope-jobs.js +65 -0
  56. package/dist/catalog/scope-sync-workers.d.ts +22 -0
  57. package/dist/catalog/scope-sync-workers.js +58 -2
  58. package/dist/catalog/scope-sync.d.ts +40 -65
  59. package/dist/catalog/scope-sync.js +114 -122
  60. package/dist/channels/channels.module.d.ts +5 -2
  61. package/dist/channels/channels.module.js +9 -2
  62. package/dist/channels/deliver-job.d.ts +25 -0
  63. package/dist/channels/deliver-job.js +25 -0
  64. package/dist/channels/inbound.d.ts +1 -1
  65. package/dist/channels/inbound.js +4 -17
  66. package/dist/channels/jobs.d.ts +28 -18
  67. package/dist/channels/jobs.js +63 -26
  68. package/dist/channels/threads.d.ts +4 -3
  69. package/dist/channels/threads.js +4 -3
  70. package/dist/config/schema.d.ts +0 -3
  71. package/dist/config/schema.js +11 -20
  72. package/dist/conversations/agent-tree.d.ts +16 -0
  73. package/dist/conversations/agent-tree.js +36 -0
  74. package/dist/conversations/conversations.controller.js +4 -2
  75. package/dist/conversations/conversations.service.d.ts +61 -19
  76. package/dist/conversations/conversations.service.js +69 -43
  77. package/dist/conversations/event-log.d.ts +7 -10
  78. package/dist/conversations/event-log.js +24 -15
  79. package/dist/conversations/events.d.ts +21 -0
  80. package/dist/conversations/events.js +26 -2
  81. package/dist/conversations/queue.service.d.ts +8 -3
  82. package/dist/conversations/queue.service.js +37 -13
  83. package/dist/conversations/source.d.ts +10 -0
  84. package/dist/conversations/source.js +17 -0
  85. package/dist/conversations/titles.d.ts +21 -8
  86. package/dist/conversations/titles.js +42 -15
  87. package/dist/conversations/tree.d.ts +10 -0
  88. package/dist/conversations/tree.js +35 -0
  89. package/dist/db/kernel-schema.d.ts +35 -22
  90. package/dist/devices/devices.service.d.ts +23 -11
  91. package/dist/devices/devices.service.js +34 -26
  92. package/dist/errors.d.ts +11 -0
  93. package/dist/errors.js +11 -0
  94. package/dist/evals/run-replay.d.ts +13 -2
  95. package/dist/evals/run-replay.js +42 -9
  96. package/dist/evals/snapshot.d.ts +7 -1
  97. package/dist/evals/snapshot.js +2 -1
  98. package/dist/extensions/extensions.module.js +3 -0
  99. package/dist/extensions/hooks.d.ts +22 -3
  100. package/dist/extensions/hooks.js +15 -1
  101. package/dist/extensions/lifecycle-hooks.d.ts +99 -0
  102. package/dist/extensions/lifecycle-hooks.js +273 -0
  103. package/dist/extensions/lifecycle.d.ts +6 -1
  104. package/dist/extensions/lifecycle.js +9 -2
  105. package/dist/extensions/org-config.d.ts +13 -1
  106. package/dist/extensions/org-config.js +19 -2
  107. package/dist/extensions/permission-view.d.ts +9 -5
  108. package/dist/extensions/permission-view.js +9 -4
  109. package/dist/extensions/point-defaults.d.ts +28 -1
  110. package/dist/extensions/point-defaults.js +44 -13
  111. package/dist/extensions/usage-subscribers.d.ts +43 -0
  112. package/dist/extensions/usage-subscribers.js +108 -0
  113. package/dist/http/api.module.js +2 -0
  114. package/dist/inbox/inbox.service.d.ts +6 -20
  115. package/dist/inbox/inbox.service.js +0 -3
  116. package/dist/inbox/org-notices.d.ts +1 -1
  117. package/dist/inbox/platform-inbox.service.d.ts +3 -32
  118. package/dist/inbox/platform-inbox.service.js +5 -54
  119. package/dist/index.d.ts +1 -0
  120. package/dist/index.js +1 -0
  121. package/dist/infra/cli/kernel-cli.module.js +5 -0
  122. package/dist/infra/events/kernel-events.d.ts +9 -35
  123. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  124. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  125. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  126. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  127. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  128. package/dist/infra/outbox/outbox.module.js +50 -0
  129. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  130. package/dist/infra/outbox/outbox.relay.js +76 -0
  131. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  132. package/dist/infra/outbox/outbox.service.js +131 -0
  133. package/dist/infra/outbox/registry.d.ts +244 -0
  134. package/dist/infra/outbox/registry.js +234 -0
  135. package/dist/infra/queue/queue-names.d.ts +8 -0
  136. package/dist/infra/queue/queue-names.js +17 -0
  137. package/dist/kernel.module.js +3 -0
  138. package/dist/local-devices/device-policy.service.d.ts +4 -0
  139. package/dist/local-devices/device-policy.service.js +61 -3
  140. package/dist/local-devices/machines.d.ts +13 -5
  141. package/dist/local-devices/machines.js +45 -7
  142. package/dist/local-devices/policy-summary.d.ts +44 -1
  143. package/dist/local-devices/policy-summary.js +69 -0
  144. package/dist/models/embeddings.d.ts +9 -9
  145. package/dist/models/embeddings.js +11 -18
  146. package/dist/models/models.module.js +3 -0
  147. package/dist/orgs/directory-sync.js +1 -1
  148. package/dist/orgs/members.service.js +1 -1
  149. package/dist/orgs/offboarding.d.ts +50 -57
  150. package/dist/orgs/offboarding.js +117 -162
  151. package/dist/orgs/orgs.module.js +3 -0
  152. package/dist/orgs/orgs.service.d.ts +11 -5
  153. package/dist/orgs/orgs.service.js +22 -6
  154. package/dist/overrides.d.ts +23 -3
  155. package/dist/overrides.js +12 -2
  156. package/dist/platform/exclusions.js +5 -0
  157. package/dist/platform/ops/admin.ops.js +18 -2
  158. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  159. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  160. package/dist/platform/ops/inbox.ops.js +4 -20
  161. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  162. package/dist/platform/ops/outbox.ops.js +125 -0
  163. package/dist/platform/ops/platform.ops.d.ts +1 -0
  164. package/dist/platform/ops/platform.ops.js +6 -3
  165. package/dist/platform/ops/schedules.ops.js +40 -1
  166. package/dist/platform/platform-ops.module.d.ts +2 -1
  167. package/dist/platform/platform-ops.module.js +3 -0
  168. package/dist/platform/route-schemas.js +3 -0
  169. package/dist/roles/worker-role.d.ts +10 -5
  170. package/dist/roles/worker-role.js +19 -9
  171. package/dist/roles/worker.module.js +11 -8
  172. package/dist/runtime/context/build.d.ts +3 -0
  173. package/dist/runtime/context/build.js +15 -0
  174. package/dist/runtime/context/plugin-context.d.ts +20 -0
  175. package/dist/runtime/context/plugin-context.js +30 -0
  176. package/dist/runtime/langgraph-loop.js +17 -5
  177. package/dist/runtime/limits.d.ts +15 -0
  178. package/dist/runtime/limits.js +25 -0
  179. package/dist/runtime/loop.d.ts +16 -3
  180. package/dist/runtime/loop.js +2 -1
  181. package/dist/runtime/maintenance.d.ts +7 -3
  182. package/dist/runtime/maintenance.js +52 -12
  183. package/dist/runtime/model-call.d.ts +20 -17
  184. package/dist/runtime/model-call.js +15 -24
  185. package/dist/runtime/plugin-context.service.d.ts +69 -0
  186. package/dist/runtime/plugin-context.service.js +208 -0
  187. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  188. package/dist/runtime/queue-lost-jobs.js +51 -0
  189. package/dist/runtime/run-control.d.ts +47 -5
  190. package/dist/runtime/run-control.js +79 -12
  191. package/dist/runtime/run-finish.d.ts +39 -12
  192. package/dist/runtime/run-finish.js +97 -14
  193. package/dist/runtime/run-jobs.d.ts +95 -13
  194. package/dist/runtime/run-jobs.js +199 -28
  195. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  196. package/dist/runtime/run-orphan-check.job.js +339 -0
  197. package/dist/runtime/run-processors.d.ts +16 -9
  198. package/dist/runtime/run-processors.js +1 -1
  199. package/dist/runtime/run-queue.d.ts +75 -53
  200. package/dist/runtime/run-queue.js +189 -263
  201. package/dist/runtime/run-steps.d.ts +1 -2
  202. package/dist/runtime/run-steps.js +8 -11
  203. package/dist/runtime/run-worker.d.ts +26 -16
  204. package/dist/runtime/run-worker.js +132 -37
  205. package/dist/runtime/runtime.module.d.ts +2 -3
  206. package/dist/runtime/runtime.module.js +7 -6
  207. package/dist/runtime/step-log.d.ts +16 -4
  208. package/dist/runtime/step-log.js +26 -6
  209. package/dist/runtime/stream.controller.d.ts +2 -4
  210. package/dist/runtime/stream.controller.js +5 -11
  211. package/dist/runtime/subagents/child.js +1 -1
  212. package/dist/runtime/subagents/definition.d.ts +2 -1
  213. package/dist/runtime/subagents/definition.js +4 -2
  214. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  215. package/dist/runtime/subagents/deliveries.js +16 -7
  216. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  217. package/dist/runtime/subagents/dispatch.js +14 -38
  218. package/dist/runtime/subagents/messages.d.ts +5 -1
  219. package/dist/runtime/subagents/messages.js +11 -3
  220. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  221. package/dist/runtime/subagents/skill-fork.js +8 -5
  222. package/dist/runtime/subagents/subagents.service.js +1 -1
  223. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  224. package/dist/runtime/subagents/tree-jobs.js +86 -0
  225. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  226. package/dist/runtime/usage-report-jobs.js +67 -119
  227. package/dist/runtime/usage-report.d.ts +28 -77
  228. package/dist/runtime/usage-report.js +35 -213
  229. package/dist/runtime/waits.d.ts +48 -198
  230. package/dist/runtime/waits.js +103 -805
  231. package/dist/sandbox/jobs.d.ts +20 -10
  232. package/dist/sandbox/jobs.js +23 -17
  233. package/dist/sandbox/sandbox.service.d.ts +17 -4
  234. package/dist/sandbox/sandbox.service.js +55 -14
  235. package/dist/schedules/calendar.d.ts +134 -0
  236. package/dist/schedules/calendar.js +410 -0
  237. package/dist/schedules/fire.d.ts +26 -7
  238. package/dist/schedules/fire.js +91 -22
  239. package/dist/schedules/job-ids.d.ts +66 -17
  240. package/dist/schedules/job-ids.js +81 -13
  241. package/dist/schedules/run-tracker.d.ts +0 -5
  242. package/dist/schedules/run-tracker.js +2 -14
  243. package/dist/schedules/scan.job.d.ts +13 -32
  244. package/dist/schedules/scan.job.js +50 -164
  245. package/dist/schedules/schedule-math.d.ts +42 -0
  246. package/dist/schedules/schedule-math.js +52 -0
  247. package/dist/schedules/scheduled-tasks.service.d.ts +22 -6
  248. package/dist/schedules/scheduled-tasks.service.js +70 -14
  249. package/dist/schedules/schedules.controller.d.ts +5 -1
  250. package/dist/schedules/schedules.controller.js +15 -1
  251. package/dist/schedules/schedules.module.js +0 -4
  252. package/dist/schedules/schedules.schemas.d.ts +35 -0
  253. package/dist/schedules/schedules.schemas.js +27 -0
  254. package/dist/schedules/store.d.ts +5 -0
  255. package/dist/schedules/store.js +20 -0
  256. package/dist/skills/changes.controller.d.ts +1 -1
  257. package/dist/skills/changes.controller.js +1 -1
  258. package/dist/tools/approvals.service.d.ts +41 -11
  259. package/dist/tools/approvals.service.js +108 -14
  260. package/dist/tools/builtin-names.d.ts +5 -3
  261. package/dist/tools/builtin-names.js +12 -4
  262. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  263. package/dist/tools/builtins/builtin-tools.js +21 -17
  264. package/dist/tools/builtins/sandbox/bash.js +9 -3
  265. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  266. package/dist/tools/builtins/sandbox/common.js +14 -1
  267. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  268. package/dist/tools/builtins/sandbox/index.js +4 -1
  269. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  270. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  271. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  272. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  273. package/dist/tools/builtins/web/extract.d.ts +2 -4
  274. package/dist/tools/builtins/web/extract.js +2 -5
  275. package/dist/tools/pipeline.d.ts +12 -3
  276. package/dist/tools/pipeline.js +6 -1
  277. package/dist/tools/questions.service.d.ts +30 -10
  278. package/dist/tools/questions.service.js +41 -27
  279. package/dist/tools/registry.d.ts +12 -3
  280. package/dist/tools/registry.js +13 -3
  281. package/dist/tools/tools.module.js +4 -3
  282. package/package.json +3 -3
  283. package/src/migrations/0118_device_policy_ops.sql +6 -0
  284. package/src/migrations/0119_outbox.sql +22 -0
  285. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
  286. package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
  287. package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
  288. package/src/migrations/0123_pgvector.sql +27 -0
  289. package/src/migrations/0124_conversation_source.sql +36 -0
  290. package/dist/catalog/platform-migration.d.ts +0 -115
  291. package/dist/catalog/platform-migration.js +0 -583
  292. package/dist/infra/queue/add-job-once.d.ts +0 -14
  293. package/dist/infra/queue/add-job-once.js +0 -16
@@ -0,0 +1,131 @@
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 { isDeepStrictEqual } from "node:util";
11
+ import { Inject, Injectable, Optional } from "@nestjs/common";
12
+ import { afterCommit } from "../database/after-commit.js";
13
+ import { KernelDb } from "../database/kernel-db.js";
14
+ import { currentStore, TransactionHost } from "../database/transaction-host.js";
15
+ import { Transactional } from "../database/transactional.decorator.js";
16
+ import { KERNEL_LOGGER } from "../tokens.js";
17
+ /** The fast path `add` hands each committed transaction's rows to (`OutboxModule` provides it). */
18
+ export const OUTBOX_FAST_PATH = Symbol("OUTBOX_FAST_PATH");
19
+ /**
20
+ * Test seam (`KernelOverride`): `true` makes a reused job id with different data throw instead of
21
+ * logging an error (spec §4, designer's call: "throws in tests, logs in production").
22
+ */
23
+ export const OUTBOX_STRICT_CONFLICTS = Symbol("OUTBOX_STRICT_CONFLICTS");
24
+ /**
25
+ * The ids each open transaction inserted, keyed by its transaction store: lives only as long as
26
+ * that transaction (a WeakMap entry, no state shared across requests).
27
+ */
28
+ const pendingIds = new WeakMap();
29
+ /**
30
+ * Writes owed work as a row in the caller's transaction (spec §3.2): the row commits or rolls back
31
+ * with the business write, and after the commit the fast path delivers it when it is due now (a
32
+ * row with a future `runAt` waits for the relay, spec §3.3).
33
+ */
34
+ let OutboxService = class OutboxService {
35
+ txHost;
36
+ db;
37
+ log;
38
+ fastPath;
39
+ strictConflicts;
40
+ constructor(txHost, db, log, fastPath, strictConflicts = false) {
41
+ this.txHost = txHost;
42
+ this.db = db;
43
+ this.log = log;
44
+ this.fastPath = fastPath;
45
+ this.strictConflicts = strictConflicts;
46
+ }
47
+ /**
48
+ * Adds the job to the outbox in the transaction in context (throws outside one). Returns true when
49
+ * it inserted a row, false when the same (queue, jobId) is already owed (a no-op; a dead row is
50
+ * not owed). A conflict whose stored data differs is a bug in the job id: it throws under strict
51
+ * conflicts, else logs an error. Undefined data is stored as JSON null.
52
+ */
53
+ async add(job) {
54
+ if (job.jobId.includes(":")) {
55
+ throw new Error(`outbox job id "${job.jobId}" must not contain ":" (BullMQ uses it as its key separator); ` +
56
+ 'join the id\'s parts with "-" instead');
57
+ }
58
+ const json = JSON.stringify(job.data ?? null);
59
+ const row = await this.db
60
+ .insertInto("outbox")
61
+ .values({
62
+ queue: job.queue,
63
+ job_name: job.name,
64
+ job_id: job.jobId,
65
+ data: json,
66
+ run_at: job.runAt ?? null,
67
+ org_id: job.orgId ?? null,
68
+ })
69
+ .onConflict((oc) => oc.columns(["queue", "job_id"]).where("dead_at", "is", null).doNothing())
70
+ .returning("id")
71
+ .executeTakeFirst();
72
+ if (row) {
73
+ // only a job due now goes through the fast path (ruling R20): a delayed one (a backstop, a
74
+ // timeout) is claimed by the relay within its next tick and added with its remaining delay,
75
+ // so the commit's caller spends no claim transaction on work due in seconds or hours
76
+ if (job.runAt === undefined || job.runAt.getTime() <= Date.now()) {
77
+ this.deliverAfterCommit(row.id);
78
+ }
79
+ return true;
80
+ }
81
+ const stored = await this.db
82
+ .selectFrom("outbox")
83
+ .select(["job_name", "data"])
84
+ .where("queue", "=", job.queue)
85
+ .where("job_id", "=", job.jobId)
86
+ .where("dead_at", "is", null)
87
+ .executeTakeFirst();
88
+ // no row: the owed one was delivered (and deleted) since the conflict; BullMQ dedupes the id
89
+ if (stored && !isDeepStrictEqual(stored.data, JSON.parse(json))) {
90
+ const msg = "outbox job id reused with different data";
91
+ const fields = {
92
+ queue: job.queue,
93
+ jobId: job.jobId,
94
+ job: job.name,
95
+ owedJob: stored.job_name,
96
+ };
97
+ if (this.strictConflicts)
98
+ throw new Error(`${msg}: ${JSON.stringify(fields)}`);
99
+ this.log.error(msg, fields);
100
+ }
101
+ return false;
102
+ }
103
+ /** Collects `id` for its transaction; the first id registers the transaction's one hint. */
104
+ deliverAfterCommit(id) {
105
+ // `mandatory` guarantees a live store here
106
+ const store = currentStore();
107
+ if (!store)
108
+ throw new Error("outbox: no transaction in context");
109
+ const ids = pendingIds.get(store);
110
+ if (ids) {
111
+ ids.push(id);
112
+ return;
113
+ }
114
+ const first = [id];
115
+ pendingIds.set(store, first);
116
+ afterCommit(() => this.fastPath(first));
117
+ }
118
+ };
119
+ __decorate([
120
+ Transactional({ propagation: "mandatory" })
121
+ ], OutboxService.prototype, "add", null);
122
+ OutboxService = __decorate([
123
+ Injectable(),
124
+ __param(0, Inject(TransactionHost)),
125
+ __param(1, Inject(KernelDb)),
126
+ __param(2, Inject(KERNEL_LOGGER)),
127
+ __param(3, Inject(OUTBOX_FAST_PATH)),
128
+ __param(4, Optional()),
129
+ __param(4, Inject(OUTBOX_STRICT_CONFLICTS))
130
+ ], OutboxService);
131
+ export { OutboxService };
@@ -0,0 +1,244 @@
1
+ import type { JobsOptions } from "bullmq";
2
+ import type { KernelQueueName } from "../queue/queue-names.js";
3
+ /** What the registry says about one job name added through the outbox (spec §3.5, §5). */
4
+ export interface OutboxJobSpec {
5
+ /** The kernel queue the job is added to, or the queues when one job name serves several. */
6
+ queue: KernelQueueName | readonly KernelQueueName[];
7
+ /** One sentence: why a duplicate delivery of this job is harmless (state check, key, id). */
8
+ idempotent: string;
9
+ /** Repo-relative path of the file whose `@Processor` handles the job. */
10
+ processedIn: string;
11
+ /**
12
+ * BullMQ options the dispatcher adds the job with over the queue defaults
13
+ * (`QUEUE_JOB_DEFAULTS`); the job id and the delay always come from the row.
14
+ */
15
+ opts?: Pick<JobsOptions, "attempts" | "backoff" | "priority" | "removeOnComplete" | "removeOnFail">;
16
+ }
17
+ /**
18
+ * Every job name added through the outbox. The task that introduces a job name adds its entry; a
19
+ * name without one does not type-check as an `OutboxJobName` (`add` does not check it at runtime).
20
+ */
21
+ export declare const OUTBOX_JOBS: {
22
+ readonly run: {
23
+ readonly queue: readonly ["cap-kernel-runs", "cap-kernel-subagent-runs"];
24
+ readonly idempotent: "the run worker starts only a run in `queued` state for this job's runId; a finished run's job only repeats the idempotent post-run ensureRun, a waiting run's does nothing";
25
+ readonly processedIn: "packages/kernel/src/runtime/run-processors.ts";
26
+ readonly opts: {
27
+ readonly attempts: 1;
28
+ readonly removeOnComplete: true;
29
+ readonly removeOnFail: true;
30
+ };
31
+ };
32
+ readonly "ensure-run": {
33
+ readonly queue: "cap-kernel-maintenance";
34
+ readonly idempotent: "ensureRun decides from the conversation's state; with nothing unconsumed it is a no-op";
35
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
36
+ readonly opts: {
37
+ readonly removeOnComplete: true;
38
+ readonly removeOnFail: true;
39
+ };
40
+ };
41
+ readonly "wait-timeout": {
42
+ readonly queue: "cap-kernel-maintenance";
43
+ readonly idempotent: "a timeout acts only on a still-pending wait (a row lock and status check) and a resume starts only a waiting run with nothing else pending";
44
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
45
+ readonly opts: {
46
+ readonly attempts: 5;
47
+ readonly backoff: {
48
+ readonly type: "exponential";
49
+ readonly delay: 5000;
50
+ };
51
+ readonly removeOnComplete: true;
52
+ readonly removeOnFail: true;
53
+ };
54
+ };
55
+ readonly "stop-tree": {
56
+ readonly queue: "cap-kernel-maintenance";
57
+ readonly idempotent: "stopping a stopped run is a no-op";
58
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
59
+ readonly opts: {
60
+ readonly attempts: 5;
61
+ readonly backoff: {
62
+ readonly type: "exponential";
63
+ readonly delay: 5000;
64
+ };
65
+ readonly removeOnComplete: true;
66
+ readonly removeOnFail: true;
67
+ };
68
+ };
69
+ readonly "run-stop": {
70
+ readonly queue: "cap-kernel-maintenance";
71
+ readonly idempotent: "stopping a finished run is a no-op";
72
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
73
+ readonly opts: {
74
+ readonly attempts: 5;
75
+ readonly backoff: {
76
+ readonly type: "exponential";
77
+ readonly delay: 5000;
78
+ };
79
+ readonly removeOnComplete: true;
80
+ readonly removeOnFail: {
81
+ readonly age: number;
82
+ readonly count: 1000;
83
+ };
84
+ };
85
+ };
86
+ readonly "sandbox-destroy-for": {
87
+ readonly queue: "cap-kernel-maintenance";
88
+ readonly idempotent: "destroying a destroyed device is a no-op";
89
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
90
+ readonly opts: {
91
+ readonly attempts: 5;
92
+ readonly backoff: {
93
+ readonly type: "exponential";
94
+ readonly delay: 30000;
95
+ };
96
+ readonly removeOnComplete: true;
97
+ readonly removeOnFail: 100;
98
+ };
99
+ };
100
+ readonly "deliver-to-parent": {
101
+ readonly queue: "cap-kernel-maintenance";
102
+ readonly idempotent: "the delivery's idempotency key is the child run id";
103
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
104
+ readonly opts: {
105
+ readonly attempts: 5;
106
+ readonly backoff: {
107
+ readonly type: "exponential";
108
+ readonly delay: 5000;
109
+ };
110
+ readonly removeOnComplete: true;
111
+ readonly removeOnFail: true;
112
+ };
113
+ };
114
+ readonly "usage-report": {
115
+ readonly queue: "cap-kernel-usage-report";
116
+ readonly idempotent: "handlers are idempotent by eventId (the event's `<conversationId>/<seq>` or the model_usage row id), and the job's `pending` list keeps a retry from reaching plugins already served";
117
+ readonly processedIn: "packages/kernel/src/runtime/run-processors.ts";
118
+ readonly opts: {
119
+ readonly attempts: 6;
120
+ readonly backoff: {
121
+ readonly type: "exponential";
122
+ readonly delay: 2000;
123
+ };
124
+ readonly removeOnComplete: {
125
+ readonly age: number;
126
+ readonly count: 100000;
127
+ };
128
+ readonly removeOnFail: {
129
+ readonly age: number;
130
+ readonly count: 100000;
131
+ };
132
+ };
133
+ };
134
+ readonly deliver: {
135
+ readonly queue: "cap-kernel-channels";
136
+ readonly idempotent: "a delivery resumes from the conversation's delivered seq under a per-conversation lock and sends with idempotency keys, so a second job sends nothing twice";
137
+ readonly processedIn: "packages/kernel/src/channels/channels.module.ts";
138
+ readonly opts: {
139
+ readonly attempts: 8;
140
+ readonly backoff: {
141
+ readonly type: "deliver";
142
+ };
143
+ readonly priority: 10;
144
+ readonly removeOnComplete: true;
145
+ readonly removeOnFail: true;
146
+ };
147
+ };
148
+ readonly "scope-item": {
149
+ readonly queue: "cap-kernel-plugin-scope";
150
+ readonly idempotent: "the sync converges each (item, org) from the scope, switches and installs it reads under the org's lock, and the installer re-checks under the org row lock, so a second run changes nothing";
151
+ readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
152
+ readonly opts: {
153
+ readonly attempts: 1;
154
+ readonly removeOnComplete: true;
155
+ readonly removeOnFail: true;
156
+ };
157
+ };
158
+ readonly "scope-item-backstop": {
159
+ readonly queue: "cap-kernel-plugin-scope";
160
+ readonly idempotent: "the sync converges each (item, org) from what it reads, as a scope-item job does, so a run after the immediate job changes nothing";
161
+ readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
162
+ readonly opts: {
163
+ readonly attempts: 5;
164
+ readonly backoff: {
165
+ readonly type: "exponential";
166
+ readonly delay: 60000;
167
+ };
168
+ readonly removeOnComplete: true;
169
+ readonly removeOnFail: true;
170
+ };
171
+ };
172
+ readonly "scope-org": {
173
+ readonly queue: "cap-kernel-plugin-scope";
174
+ readonly idempotent: "the sync converges each (item, org) of the org from what it reads, so a second run changes nothing";
175
+ readonly processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts";
176
+ readonly opts: {
177
+ readonly attempts: 1;
178
+ readonly removeOnComplete: true;
179
+ readonly removeOnFail: true;
180
+ };
181
+ };
182
+ readonly "owned-plugin-cleanup": {
183
+ readonly queue: "cap-kernel-maintenance";
184
+ readonly idempotent: "the cleanup takes the extension out only while no live item uses it, and a missing install or a released claim changes nothing";
185
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
186
+ readonly opts: {
187
+ readonly attempts: 3;
188
+ readonly backoff: {
189
+ readonly type: "exponential";
190
+ readonly delay: 5000;
191
+ };
192
+ readonly removeOnComplete: true;
193
+ readonly removeOnFail: true;
194
+ };
195
+ };
196
+ readonly "sched-fire": {
197
+ readonly queue: "cap-kernel-maintenance";
198
+ readonly idempotent: "a fire acts only on a pending task run, under its row lock; a retried start reuses what the first one made";
199
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
200
+ readonly opts: {
201
+ readonly attempts: 3;
202
+ readonly backoff: {
203
+ readonly type: "exponential";
204
+ readonly delay: 2000;
205
+ };
206
+ readonly removeOnComplete: true;
207
+ readonly removeOnFail: true;
208
+ };
209
+ };
210
+ readonly "sched-settle": {
211
+ readonly queue: "cap-kernel-maintenance";
212
+ readonly idempotent: "a check acts only on a task run still pending (claimed for the claimed check, unclaimed for the unfired one), under the task and run row locks, so a run claimed, started, ended or skipped since is left as it is";
213
+ readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
214
+ readonly opts: {
215
+ readonly attempts: 5;
216
+ readonly backoff: {
217
+ readonly type: "exponential";
218
+ readonly delay: 5000;
219
+ };
220
+ readonly removeOnComplete: true;
221
+ readonly removeOnFail: true;
222
+ };
223
+ };
224
+ readonly lifecycle: {
225
+ readonly queue: "cap-kernel-lifecycle";
226
+ readonly idempotent: "lifecycle handlers must be idempotent (the SDK contract): they delete what the plugin holds for the removed member, agent or org, and deleting it again is a no-op";
227
+ readonly processedIn: "packages/kernel/src/extensions/lifecycle-hooks.ts";
228
+ readonly opts: {
229
+ readonly attempts: 15;
230
+ readonly backoff: {
231
+ readonly type: "exponential";
232
+ readonly delay: 2000;
233
+ };
234
+ readonly removeOnComplete: true;
235
+ readonly removeOnFail: {
236
+ readonly age: number;
237
+ readonly count: 1000;
238
+ };
239
+ };
240
+ };
241
+ };
242
+ export type OutboxJobName = keyof typeof OUTBOX_JOBS;
243
+ /** The registry's BullMQ options of a job name (none for a name it does not list). */
244
+ export declare function outboxJobOptions(name: string): OutboxJobSpec["opts"];
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Every job name added through the outbox. The task that introduces a job name adds its entry; a
3
+ * name without one does not type-check as an `OutboxJobName` (`add` does not check it at runtime).
4
+ */
5
+ export const OUTBOX_JOBS = {
6
+ // `runtime/run-jobs.ts` RUN_JOB: one name on both run queues (a sub-agent's runs go to the
7
+ // sub-agent queue), under the job ids `runJobId` builds and, for a waiting run's resume
8
+ // (`RunResume.resumeIn`), `resumeJobId`
9
+ run: {
10
+ queue: ["cap-kernel-runs", "cap-kernel-subagent-runs"],
11
+ idempotent: "the run worker starts only a run in `queued` state for this job's runId; a finished run's job only repeats the idempotent post-run ensureRun, a waiting run's does nothing",
12
+ processedIn: "packages/kernel/src/runtime/run-processors.ts",
13
+ // one attempt (the run's own paths handle failures); removed once done, so the next run of the
14
+ // same trigger can take the id again (the post-run ensureRun, R34)
15
+ opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
16
+ },
17
+ // `runtime/run-jobs.ts` ENSURE_RUN_JOB: the ensure-run backstop owed with every message, queue
18
+ // change, background task finish, sub-agent delivery and run end (spec §6), under the job ids
19
+ // `ensureRunJobId` builds
20
+ "ensure-run": {
21
+ queue: "cap-kernel-maintenance",
22
+ idempotent: "ensureRun decides from the conversation's state; with nothing unconsumed it is a no-op",
23
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
24
+ // removed once done (the queue defaults keep finished jobs), so a later write under the same
25
+ // seq (a queue change writes no event of its own) owes a job that is not deduped away
26
+ opts: { removeOnComplete: true, removeOnFail: true },
27
+ },
28
+ // `runtime/waits.ts` WAIT_TIMEOUT_JOB: owed with the suspend that sets a run `waiting`, under
29
+ // `waitTimeoutJobId`, with `runAt` = the wait's deadline (spec §6 "Resumes and timeouts")
30
+ "wait-timeout": {
31
+ queue: "cap-kernel-maintenance",
32
+ idempotent: "a timeout acts only on a still-pending wait (a row lock and status check) and a resume starts only a waiting run with nothing else pending",
33
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
34
+ // retried while PG is briefly unavailable; removed once done
35
+ opts: {
36
+ attempts: 5,
37
+ backoff: { type: "exponential", delay: 5_000 },
38
+ removeOnComplete: true,
39
+ removeOnFail: true,
40
+ },
41
+ },
42
+ // `runtime/subagents/tree-jobs.ts` STOP_TREE_JOB: owed by a run's terminal transaction that
43
+ // leaves an active fork child (or a person-stopped sub-agent's descendants), under
44
+ // `stopChildrenJobId`, and by a root's delete, under `stopDeletedTreeJobId` (spec §6)
45
+ "stop-tree": {
46
+ queue: "cap-kernel-maintenance",
47
+ idempotent: "stopping a stopped run is a no-op",
48
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
49
+ // retried while PG is briefly unavailable; removed once done
50
+ opts: {
51
+ attempts: 5,
52
+ backoff: { type: "exponential", delay: 5_000 },
53
+ removeOnComplete: true,
54
+ removeOnFail: true,
55
+ },
56
+ },
57
+ // `orgs/offboarding.ts` RUN_STOP_JOB: owed by the offboard transaction for each of the departed
58
+ // member's active runs in the org, under `runStopJobId` (spec §8.1)
59
+ "run-stop": {
60
+ queue: "cap-kernel-maintenance",
61
+ idempotent: "stopping a finished run is a no-op",
62
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
63
+ // retried while PG or Redis is briefly unavailable; removed once done; a job that failed every
64
+ // attempt is kept a week (no sweep stops the run after it: the failure must stay visible)
65
+ opts: {
66
+ attempts: 5,
67
+ backoff: { type: "exponential", delay: 5_000 },
68
+ removeOnComplete: true,
69
+ removeOnFail: { age: 7 * 86_400, count: 1_000 },
70
+ },
71
+ },
72
+ // `sandbox/jobs.ts` SANDBOX_DESTROY_FOR_JOB: owed by the transaction that soft-deletes a device
73
+ // (its deletion, offboarding), under `deviceDestroyJobId` (spec §8.1)
74
+ "sandbox-destroy-for": {
75
+ queue: "cap-kernel-maintenance",
76
+ idempotent: "destroying a destroyed device is a no-op",
77
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
78
+ // the options the direct enqueue had: retried while the provider is down
79
+ opts: {
80
+ attempts: 5,
81
+ backoff: { type: "exponential", delay: 30_000 },
82
+ removeOnComplete: true,
83
+ removeOnFail: 100,
84
+ },
85
+ },
86
+ // `runtime/subagents/tree-jobs.ts` DELIVER_TO_PARENT_JOB: owed by a sub-agent run's terminal
87
+ // transaction, under `deliverToParentJobId` (spec §6)
88
+ "deliver-to-parent": {
89
+ queue: "cap-kernel-maintenance",
90
+ idempotent: "the delivery's idempotency key is the child run id",
91
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
92
+ opts: {
93
+ attempts: 5,
94
+ backoff: { type: "exponential", delay: 5_000 },
95
+ removeOnComplete: true,
96
+ removeOnFail: true,
97
+ },
98
+ },
99
+ // `runtime/usage-report-jobs.ts` USAGE_REPORT_JOB: owed with the usage it reports (a `usage`
100
+ // event or a `model_usage` row) while some plugin subscribes, under `usageReportJobId` /
101
+ // `modelUsageJobId` (spec §8.1)
102
+ "usage-report": {
103
+ queue: "cap-kernel-usage-report",
104
+ idempotent: "handlers are idempotent by eventId (the event's `<conversationId>/<seq>` or the model_usage row id), and the job's `pending` list keeps a retry from reaching plugins already served",
105
+ processedIn: "packages/kernel/src/runtime/run-processors.ts",
106
+ // first attempt plus 5 retries, 2 s doubling (F11); finished jobs kept a day so a re-add of the
107
+ // same id (a crash between the add and the row's delete) dedupes; failed ones a week; both
108
+ // capped by count (I-2: a long outage of a billing endpoint cannot grow Redis)
109
+ opts: {
110
+ attempts: 6,
111
+ backoff: { type: "exponential", delay: 2_000 },
112
+ removeOnComplete: { age: 24 * 3600, count: 100_000 },
113
+ removeOnFail: { age: 7 * 86_400, count: 100_000 },
114
+ },
115
+ },
116
+ // `channels/deliver-job.ts` DELIVER_JOB: owed by every event append of a conversation with a
117
+ // channel target (a reply, an approval decision, a question answer, a wait stop) and by binding
118
+ // a conversation to its target, under `deliverJobId` (spec §8.1)
119
+ deliver: {
120
+ queue: "cap-kernel-channels",
121
+ idempotent: "a delivery resumes from the conversation's delivered seq under a per-conversation lock and sends with idempotency keys, so a second job sends nothing twice",
122
+ processedIn: "packages/kernel/src/channels/channels.module.ts",
123
+ // the platform's retry-after, else exponential (the processor's `deliver` backoff strategy);
124
+ // behind inbound jobs; removed once done, so a later append's id is never deduped away
125
+ opts: {
126
+ attempts: 8,
127
+ backoff: { type: "deliver" },
128
+ priority: 10,
129
+ removeOnComplete: true,
130
+ removeOnFail: true,
131
+ },
132
+ },
133
+ // `catalog/scope-sync.ts` SCOPE_ITEM_JOB: owed by every transaction that changes an item's
134
+ // scope, disabled flag, list, owner or platform version, under `scopeItemJobId` (one per scope
135
+ // version), and by a sync deferred on a replica that had not loaded the extension yet, under
136
+ // `scopeRetryJobId` with `runAt` (spec §8.1)
137
+ "scope-item": {
138
+ queue: "cap-kernel-plugin-scope",
139
+ idempotent: "the sync converges each (item, org) from the scope, switches and installs it reads under the org's lock, and the installer re-checks under the org row lock, so a second run changes nothing",
140
+ processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
141
+ // one attempt (a pair's failure is recorded, not retried); removed once done, so the next
142
+ // add of the same id is not swallowed
143
+ opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
144
+ },
145
+ // `catalog/scope-jobs.ts` SCOPE_BACKSTOP_JOB: owed by a confirmation's commit (an import, a
146
+ // permissions confirm, an org upload's first version) with `runAt` 5 min on, under
147
+ // `scopeBackstopJobId`, so a crash before the post-deploy add still converges (ruling R24)
148
+ "scope-item-backstop": {
149
+ queue: "cap-kernel-plugin-scope",
150
+ idempotent: "the sync converges each (item, org) from what it reads, as a scope-item job does, so a run after the immediate job changes nothing",
151
+ processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
152
+ // retried while the item's extension is not enabled on the deployment yet (the job throws):
153
+ // 1, 2, 4, 8 min; removed once done
154
+ opts: {
155
+ attempts: 5,
156
+ backoff: { type: "exponential", delay: 60_000 },
157
+ removeOnComplete: true,
158
+ removeOnFail: true,
159
+ },
160
+ },
161
+ // `catalog/scope-sync.ts` SCOPE_ORG_JOB: owed by an org's creation, under `scopeOrgJobId`
162
+ "scope-org": {
163
+ queue: "cap-kernel-plugin-scope",
164
+ idempotent: "the sync converges each (item, org) of the org from what it reads, so a second run changes nothing",
165
+ processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
166
+ opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
167
+ },
168
+ // `catalog/job-ids.ts` OWNED_PLUGIN_CLEANUP_JOB: owed by the removal of an org-owned plugin
169
+ // (and by a confirmation that took out the extension of an item removed meanwhile), under
170
+ // `ownedPluginCleanupJobId` (spec §8.1)
171
+ "owned-plugin-cleanup": {
172
+ queue: "cap-kernel-maintenance",
173
+ idempotent: "the cleanup takes the extension out only while no live item uses it, and a missing install or a released claim changes nothing",
174
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
175
+ // a few retries while PG is briefly unavailable; removed once done
176
+ opts: {
177
+ attempts: 3,
178
+ backoff: { type: "exponential", delay: 5_000 },
179
+ removeOnComplete: true,
180
+ removeOnFail: true,
181
+ },
182
+ },
183
+ // `schedules/job-ids.ts` SCHED_FIRE_JOB: owed by the transaction that writes a pending task run
184
+ // (the scan's tick, a person's "run now"), under `schedFireJobId` (spec §8.1)
185
+ "sched-fire": {
186
+ queue: "cap-kernel-maintenance",
187
+ idempotent: "a fire acts only on a pending task run, under its row lock; a retried start reuses what the first one made",
188
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
189
+ // a few retries while PG is briefly unavailable; removed once done
190
+ opts: {
191
+ attempts: 3,
192
+ backoff: { type: "exponential", delay: 2_000 },
193
+ removeOnComplete: true,
194
+ removeOnFail: true,
195
+ },
196
+ },
197
+ // `schedules/job-ids.ts` SCHED_SETTLE_JOB: the claimed check, owed by the fire job's claim (the
198
+ // transaction that writes `fired_at`) with `runAt` 10 min on, under `schedSettleJobId` (a run
199
+ // still in flight owes the next check an hour on); the unfired check, owed with the task run
200
+ // (scan tick, "run now") under `schedSettleUnfiredJobId` (ruling R26)
201
+ "sched-settle": {
202
+ queue: "cap-kernel-maintenance",
203
+ idempotent: "a check acts only on a task run still pending (claimed for the claimed check, unclaimed for the unfired one), under the task and run row locks, so a run claimed, started, ended or skipped since is left as it is",
204
+ processedIn: "packages/kernel/src/runtime/maintenance.ts",
205
+ // retried while PG is briefly unavailable; removed once done
206
+ opts: {
207
+ attempts: 5,
208
+ backoff: { type: "exponential", delay: 5_000 },
209
+ removeOnComplete: true,
210
+ removeOnFail: true,
211
+ },
212
+ },
213
+ // `extensions/lifecycle-hooks.ts` LIFECYCLE_JOB: owed by the transaction that removes a member,
214
+ // deletes an agent or deletes an org, once per subscribing plugin, under `lifecycleJobId`
215
+ // (memory plugin spec §6.3)
216
+ lifecycle: {
217
+ queue: "cap-kernel-lifecycle",
218
+ idempotent: "lifecycle handlers must be idempotent (the SDK contract): they delete what the plugin holds for the removed member, agent or org, and deleting it again is a no-op",
219
+ processedIn: "packages/kernel/src/extensions/lifecycle-hooks.ts",
220
+ // retried while the plugin is not loaded on the replica that took the job (a rolling deploy)
221
+ // or its handler fails: 15 attempts from 2 s, doubling (about 18 h in all); removed once done;
222
+ // a job that failed every attempt is kept a week (the failure must stay visible)
223
+ opts: {
224
+ attempts: 15,
225
+ backoff: { type: "exponential", delay: 2_000 },
226
+ removeOnComplete: true,
227
+ removeOnFail: { age: 7 * 86_400, count: 1_000 },
228
+ },
229
+ },
230
+ };
231
+ /** The registry's BullMQ options of a job name (none for a name it does not list). */
232
+ export function outboxJobOptions(name) {
233
+ return OUTBOX_JOBS[name]?.opts;
234
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The kernel's own BullMQ queues, spelled as literals so infra code (the outbox registry and
3
+ * dispatcher) names a queue without importing feature modules. `KERNEL_QUEUE_NAMES`
4
+ * (admin/health.service.ts) lists the same names from the feature constants;
5
+ * test/infra/queue-names.test.ts keeps the two equal.
6
+ */
7
+ export declare const KERNEL_QUEUES: readonly ["cap-kernel-runs", "cap-kernel-subagent-runs", "cap-kernel-channels", "cap-kernel-maintenance", "cap-kernel-plugin-scope", "cap-kernel-titles", "cap-kernel-usage-report", "cap-kernel-evals", "cap-kernel-lifecycle"];
8
+ export type KernelQueueName = (typeof KERNEL_QUEUES)[number];
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The kernel's own BullMQ queues, spelled as literals so infra code (the outbox registry and
3
+ * dispatcher) names a queue without importing feature modules. `KERNEL_QUEUE_NAMES`
4
+ * (admin/health.service.ts) lists the same names from the feature constants;
5
+ * test/infra/queue-names.test.ts keeps the two equal.
6
+ */
7
+ export const KERNEL_QUEUES = [
8
+ "cap-kernel-runs",
9
+ "cap-kernel-subagent-runs",
10
+ "cap-kernel-channels",
11
+ "cap-kernel-maintenance",
12
+ "cap-kernel-plugin-scope",
13
+ "cap-kernel-titles",
14
+ "cap-kernel-usage-report",
15
+ "cap-kernel-evals",
16
+ "cap-kernel-lifecycle",
17
+ ];
@@ -25,6 +25,7 @@ import { databaseCheck, HEALTH_CHECKS, redisCheck } from "./infra/health/health.
25
25
  import { GracefulShutdownModule } from "./infra/lifecycle/graceful-shutdown.module.js";
26
26
  import { LockModule } from "./infra/lock/lock.module.js";
27
27
  import { LoggingModule } from "./infra/logging/logging.module.js";
28
+ import { OutboxModule } from "./infra/outbox/outbox.module.js";
28
29
  import { PresenceModule } from "./infra/presence/presence.module.js";
29
30
  import { QueueModule } from "./infra/queue/queue.module.js";
30
31
  import { RedisModule } from "./infra/redis/redis.module.js";
@@ -95,6 +96,8 @@ let KernelModule = KernelModule_1 = class KernelModule {
95
96
  ConfigModule.forRoot(config, options.env !== undefined ? { env: options.env } : {}),
96
97
  GracefulShutdownModule,
97
98
  DatabaseModule.forRoot(),
99
+ // owed jobs written in the caller's transaction (transactional outbox spec §3.2)
100
+ OutboxModule,
98
101
  RedisModule,
99
102
  LockModule,
100
103
  CacheModule,
@@ -53,6 +53,10 @@ export declare class DevicePolicyService {
53
53
  features: DeviceFeature[];
54
54
  policy: DeviceLocalPolicySummary | null;
55
55
  } | null>;
56
+ /** Adds `op` to the machine's pending ops, dropping the expired ones. */
57
+ private notePending;
58
+ /** Removes the pending op `opId` from the machine's row. */
59
+ private dropPending;
56
60
  /**
57
61
  * Keeps the policy the machine wrote on its row and audits the change (`confirmed`); with no
58
62
  * policy (the answer never came) only audits the attempt, unconfirmed. The detail's `isolation`