@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
@@ -1,7 +1,7 @@
1
1
  import { ApiError } from "../../errors.js";
2
2
  import { AGENT_STOPPED_WITH_RUN_TEXT } from "../../tools/builtins/agent.js";
3
3
  import { subagentsWait } from "../loop.js";
4
- import { TERMINAL_STATUSES } from "../run-finish.js";
4
+ import { TERMINAL_STATUSES } from "../run-jobs.js";
5
5
  import { childResult, dispatchOfCall, finishedEvent } from "./child.js";
6
6
  import { dispatchSubagent } from "./dispatch.js";
7
7
  /**
@@ -10,8 +10,8 @@ import { dispatchSubagent } from "./dispatch.js";
10
10
  * through phase C's `dispatchSubagent` before any model call and waits on it (`subagents` wait
11
11
  * `sa-<runId>-0`; the dispatch row has `for_skill`, agent map spec §5.4: a fork stays in the
12
12
  * foreground while `Agent` calls run in the background). When the child has ended, `prepare` appends the model-visible
13
- * `skill_fork_result` and the parent's model answers. Every terminal path of the run (and the wait
14
- * sweep) pairs a fork invocation without a report ({@link pairStoppedForks}), so the log never holds
13
+ * `skill_fork_result` and the parent's model answers. Every terminal path of the run
14
+ * (`RunFinish.finishIn`) pairs a fork invocation without a report ({@link pairStoppedForks}), so the log never holds
15
15
  * a fork lead whose report is missing before the next model call.
16
16
  */
17
17
  /** The dispatch's parent call id (no `:`; there is no tool call). */
@@ -125,8 +125,11 @@ export async function advanceSkillForks(d, a) {
125
125
  agentVersion: ctx.agentVersion,
126
126
  agent: ctx.agent,
127
127
  accountId: ctx.triggerAccountId,
128
+ source: ctx.source,
129
+ ownerAccountId: ctx.ownerAccountId,
130
+ role: ctx.role,
128
131
  signal: ctx.signal,
129
- deadline: ctx.limits.deadline,
132
+ ...(ctx.limits.deadline !== undefined && { deadline: ctx.limits.deadline }),
130
133
  ...(ctx.conversationKind !== undefined &&
131
134
  ctx.conversationKind !== "eval" && { conversationKind: ctx.conversationKind }),
132
135
  },
@@ -188,7 +191,7 @@ export const recordedSkillForks = (eventLog, recorded) => ({
188
191
  },
189
192
  });
190
193
  /**
191
- * In the transaction of a run's terminal path (or the wait sweep): the run's fork invocation
194
+ * In the transaction of a run's terminal path (`RunFinish.finishIn`): the run's fork invocation
192
195
  * (its trigger message, by the run boundary) without a report gets one now: the ended child's
193
196
  * result (and the parent's missing `subagent_finished`), {@link AGENT_STOPPED_WITH_RUN_TEXT} for a
194
197
  * child still active (the caller stops it after its commit, as for `Agent` calls), or
@@ -53,7 +53,7 @@ let SubagentsService = class SubagentsService {
53
53
  purposes,
54
54
  registry,
55
55
  config,
56
- enqueue: (data, jobId, kind) => jobs.enqueue(data, jobId, kind),
56
+ owe: (data, jobId, kind, orgId) => jobs.owe(data, jobId, kind, orgId),
57
57
  log,
58
58
  invoker,
59
59
  commitDispatch: (a, plan, child) => this.commitDispatch(a, plan, child),
@@ -0,0 +1,61 @@
1
+ import type { OutboxService } from "../../infra/outbox/outbox.service.js";
2
+ import type { Waits } from "../waits.js";
3
+ /**
4
+ * The stops a transaction owes a sub-agent tree (transactional outbox spec §6 "Sub-agents"), on
5
+ * the maintenance queue. Two shapes:
6
+ * - `{ runId }`: what a run's terminal transaction owes its children: its active fork invocation
7
+ * child stops (`Waits.stopChildren`, sub-agents spec §12), and a sub-agent run a person stopped
8
+ * stops its descendants (`Waits.stopDescendantsOfStopped`, agent map spec §6);
9
+ * - `{ conversationId, byAccountId }`: a deleted root's whole tree below it stops
10
+ * (`Waits.stopTree` without the root, agent map spec §10).
11
+ *
12
+ * The caller still stops them right after its commit; the job is the backstop for a process that
13
+ * died in between, {@link TREE_JOB_BACKSTOP_MS} later.
14
+ */
15
+ export declare const STOP_TREE_JOB = "stop-tree";
16
+ /**
17
+ * A finished sub-agent run's delivery to its parent (`Waits.childFinished`: a background child's
18
+ * `subagent_finished` and `finished` delivery, a fork child's `subagent_finished` and its parent
19
+ * run's resume), owed by the child run's terminal transaction. The delivery is not written in that
20
+ * transaction: it holds the child's conversation and run rows, and the parent's conversation row
21
+ * comes before the child's in the dispatch's lock order (parent conversation, parent run, budget
22
+ * run, child conversation), so taking it there could deadlock with a follow-up dispatch. The
23
+ * caller delivers right after its commit; the job is the backstop, {@link TREE_JOB_BACKSTOP_MS}
24
+ * later.
25
+ */
26
+ export declare const DELIVER_TO_PARENT_JOB = "deliver-to-parent";
27
+ /** How long after its transaction a tree job fires (a backstop of the direct call, not a second). */
28
+ export declare const TREE_JOB_BACKSTOP_MS = 30000;
29
+ /** The `stop-tree` job's data. */
30
+ export type StopTreeJobData = {
31
+ runId: string;
32
+ } | {
33
+ conversationId: string;
34
+ byAccountId: string | null;
35
+ };
36
+ /** The `deliver-to-parent` job's data. */
37
+ export interface DeliverToParentJobData {
38
+ childRunId: string;
39
+ }
40
+ /** The `stop-tree` job id of a finished run's children (no `:`). */
41
+ export declare const stopChildrenJobId: (runId: string) => string;
42
+ /** The `stop-tree` job id of a deleted root's tree (a conversation is deleted once; no `:`). */
43
+ export declare const stopDeletedTreeJobId: (conversationId: string) => string;
44
+ /** The `deliver-to-parent` job id: one per child run (no `:`). */
45
+ export declare const deliverToParentJobId: (childRunId: string) => string;
46
+ /** Owes the stops of `runId`'s children in the caller's transaction (throws outside one). */
47
+ export declare function oweStopChildren(outbox: Pick<OutboxService, "add">, runId: string, orgId: string): Promise<boolean>;
48
+ /** Owes the stop of a deleted root's tree in the caller's transaction (throws outside one). */
49
+ export declare function oweStopDeletedTree(outbox: Pick<OutboxService, "add">, conversationId: string, byAccountId: string | null, orgId: string): Promise<boolean>;
50
+ /** Owes a finished child run's delivery to its parent in the caller's transaction. */
51
+ export declare function oweDeliverToParent(outbox: Pick<OutboxService, "add">, childRunId: string, orgId: string): Promise<boolean>;
52
+ /**
53
+ * The `stop-tree` job (`MaintenanceProcessor`): every stop reads the active runs and stops only
54
+ * those, so a run the direct call already stopped is not stopped again.
55
+ */
56
+ export declare function processStopTree(waits: Pick<Waits, "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, data: StopTreeJobData): Promise<void>;
57
+ /**
58
+ * The `deliver-to-parent` job (`MaintenanceProcessor`): `Waits.childFinished` writes the delivery
59
+ * once by the child run (idempotency keys under the parent conversation's row lock).
60
+ */
61
+ export declare function processDeliverToParent(waits: Pick<Waits, "childFinished">, data: DeliverToParentJobData): Promise<void>;
@@ -0,0 +1,86 @@
1
+ import { MAINTENANCE_QUEUE_NAME } from "../maintenance-queue.js";
2
+ /**
3
+ * The stops a transaction owes a sub-agent tree (transactional outbox spec §6 "Sub-agents"), on
4
+ * the maintenance queue. Two shapes:
5
+ * - `{ runId }`: what a run's terminal transaction owes its children: its active fork invocation
6
+ * child stops (`Waits.stopChildren`, sub-agents spec §12), and a sub-agent run a person stopped
7
+ * stops its descendants (`Waits.stopDescendantsOfStopped`, agent map spec §6);
8
+ * - `{ conversationId, byAccountId }`: a deleted root's whole tree below it stops
9
+ * (`Waits.stopTree` without the root, agent map spec §10).
10
+ *
11
+ * The caller still stops them right after its commit; the job is the backstop for a process that
12
+ * died in between, {@link TREE_JOB_BACKSTOP_MS} later.
13
+ */
14
+ export const STOP_TREE_JOB = "stop-tree";
15
+ /**
16
+ * A finished sub-agent run's delivery to its parent (`Waits.childFinished`: a background child's
17
+ * `subagent_finished` and `finished` delivery, a fork child's `subagent_finished` and its parent
18
+ * run's resume), owed by the child run's terminal transaction. The delivery is not written in that
19
+ * transaction: it holds the child's conversation and run rows, and the parent's conversation row
20
+ * comes before the child's in the dispatch's lock order (parent conversation, parent run, budget
21
+ * run, child conversation), so taking it there could deadlock with a follow-up dispatch. The
22
+ * caller delivers right after its commit; the job is the backstop, {@link TREE_JOB_BACKSTOP_MS}
23
+ * later.
24
+ */
25
+ export const DELIVER_TO_PARENT_JOB = "deliver-to-parent";
26
+ /** How long after its transaction a tree job fires (a backstop of the direct call, not a second). */
27
+ export const TREE_JOB_BACKSTOP_MS = 30_000;
28
+ /** The `stop-tree` job id of a finished run's children (no `:`). */
29
+ export const stopChildrenJobId = (runId) => `stop-tree-run-${runId}`;
30
+ /** The `stop-tree` job id of a deleted root's tree (a conversation is deleted once; no `:`). */
31
+ export const stopDeletedTreeJobId = (conversationId) => `stop-tree-deleted-${conversationId}`;
32
+ /** The `deliver-to-parent` job id: one per child run (no `:`). */
33
+ export const deliverToParentJobId = (childRunId) => `deliver-to-parent-${childRunId}`;
34
+ const backstopAt = () => new Date(Date.now() + TREE_JOB_BACKSTOP_MS);
35
+ /** Owes the stops of `runId`'s children in the caller's transaction (throws outside one). */
36
+ export function oweStopChildren(outbox, runId, orgId) {
37
+ return outbox.add({
38
+ queue: MAINTENANCE_QUEUE_NAME,
39
+ name: STOP_TREE_JOB,
40
+ jobId: stopChildrenJobId(runId),
41
+ data: { runId },
42
+ runAt: backstopAt(),
43
+ orgId,
44
+ });
45
+ }
46
+ /** Owes the stop of a deleted root's tree in the caller's transaction (throws outside one). */
47
+ export function oweStopDeletedTree(outbox, conversationId, byAccountId, orgId) {
48
+ return outbox.add({
49
+ queue: MAINTENANCE_QUEUE_NAME,
50
+ name: STOP_TREE_JOB,
51
+ jobId: stopDeletedTreeJobId(conversationId),
52
+ data: { conversationId, byAccountId },
53
+ runAt: backstopAt(),
54
+ orgId,
55
+ });
56
+ }
57
+ /** Owes a finished child run's delivery to its parent in the caller's transaction. */
58
+ export function oweDeliverToParent(outbox, childRunId, orgId) {
59
+ return outbox.add({
60
+ queue: MAINTENANCE_QUEUE_NAME,
61
+ name: DELIVER_TO_PARENT_JOB,
62
+ jobId: deliverToParentJobId(childRunId),
63
+ data: { childRunId },
64
+ runAt: backstopAt(),
65
+ orgId,
66
+ });
67
+ }
68
+ /**
69
+ * The `stop-tree` job (`MaintenanceProcessor`): every stop reads the active runs and stops only
70
+ * those, so a run the direct call already stopped is not stopped again.
71
+ */
72
+ export async function processStopTree(waits, data) {
73
+ if ("runId" in data) {
74
+ await waits.stopChildren(data.runId, null);
75
+ await waits.stopDescendantsOfStopped(data.runId);
76
+ return;
77
+ }
78
+ await waits.stopTree(data.conversationId, data.byAccountId, { includeSelf: false });
79
+ }
80
+ /**
81
+ * The `deliver-to-parent` job (`MaintenanceProcessor`): `Waits.childFinished` writes the delivery
82
+ * once by the child run (idempotency keys under the parent conversation's row lock).
83
+ */
84
+ export async function processDeliverToParent(waits, data) {
85
+ await waits.childFinished(data.childRunId);
86
+ }
@@ -1,20 +1,17 @@
1
- import type { Logger } from "@capybara-hq/plugin-sdk";
2
- import { type OnApplicationShutdown } from "@nestjs/common";
3
- import type { JobsOptions, Queue } from "bullmq";
4
- import type { KernelSchemaDb } from "../db/kernel-schema.js";
5
- import { KernelConfigService } from "../infra/config/kernel-config.service.js";
1
+ import type { ConversationEvent } from "../conversations/events.js";
2
+ import { UsageSubscribers } from "../extensions/usage-subscribers.js";
6
3
  import { KernelDb } from "../infra/database/kernel-db.js";
7
- import { KernelEvents } from "../infra/events/kernel-events.js";
4
+ import { TransactionHost } from "../infra/database/transaction-host.js";
5
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
8
6
  /**
9
- * The producer half of `usage.report` delivery for `model_usage` rows, and the queue's ids and job
10
- * options shared with the reporter (`usage-report.ts`). The `ctx.models` broker sits below the
11
- * extension host and must not register a queue there (CLI contexts that build the extensions
12
- * module never connect to Redis eagerly), so it only announces a row (`model_usage.recorded`) and
13
- * {@link ModelUsageJobs} enqueues it here.
7
+ * The producer half of `usage.report` delivery, and the queue's ids shared with the reporter
8
+ * (`usage-report.ts`): a delivery is owed in the transaction that writes the usage it reports
9
+ * (transactional outbox spec §8.1). It registers no queue (the outbox dispatcher adds the job), so
10
+ * the `ctx.models` broker below the extension host and the step writes can use it.
14
11
  */
15
12
  /** Queue of `usage.report` deliveries (no `:` in the name). */
16
13
  export declare const USAGE_REPORT_QUEUE = "cap-kernel-usage-report";
17
- /** The job name of every delivery. */
14
+ /** The job name of every delivery (its outbox registry entry holds the job options). */
18
15
  export declare const USAGE_REPORT_JOB = "usage-report";
19
16
  /** One job per `usage` event (no `:`: conversation ids are UUIDs, seq is a number). */
20
17
  export declare const usageReportJobId: (conversationId: string, seq: number) => string;
@@ -42,47 +39,25 @@ export type UsageReportJob = ({
42
39
  */
43
40
  dropped?: Record<string, string>;
44
41
  };
45
- /** First attempt plus 5 retries (F11). */
46
- export declare const USAGE_REPORT_ATTEMPTS = 6;
47
- /** Retry backoff: 2 s, doubling (2, 4, 8, 16, 32 s). */
48
- export declare const USAGE_REPORT_BACKOFF_MS = 2000;
49
- /** The delivery retry policy: attempts and the first exponential backoff. */
50
- export interface UsageReportRetry {
51
- attempts: number;
52
- backoffMs: number;
53
- }
54
- /** Optional provider: the {@link UsageReportRetry} instead of the defaults above (tests). */
55
- export declare const USAGE_REPORT_RETRY: unique symbol;
56
- /** The options of one delivery job: its id, the retry policy and the retention of finished jobs. */
57
- export declare function usageReportJobOpts(jobId: string, retry: UsageReportRetry, keepHours: number): JobsOptions;
58
- /**
59
- * Plugins that subscribe to `usage.report` according to deployment state: an approved permission
60
- * snapshot that declares the `usage.report` hook, for a plugin the deployment still knows: an enabled
61
- * install, or a config-declared plugin with a replica status row that is not `unloaded`. With `orgId`, only plugins
62
- * the organization enabled (org config rows and required bootstrap plugins).
63
- */
64
- export declare function queryUsageSubscribers(db: KernelSchemaDb, orgId?: string): Promise<Set<string>>;
65
42
  /**
66
- * Enqueues the `usage.report` delivery of `model_usage` rows on `model_usage.recorded` (fast path;
67
- * the reporter's sweep re-enqueues rows still unreported, so a lost event or enqueue is caught up).
43
+ * Owes `usage.report` deliveries in the caller's transaction, only while the deployment has a
44
+ * subscriber ({@link UsageSubscribers}): usage written while no plugin subscribes is never
45
+ * reported.
68
46
  */
69
- export declare class ModelUsageJobs implements OnApplicationShutdown {
47
+ export declare class UsageReportJobs {
70
48
  private readonly kdb;
71
- private readonly queue;
72
- private readonly config;
73
- private readonly log;
74
- private readonly retry;
75
- private readonly unsubscribe;
76
- /** Set at shutdown: a late event enqueues nothing (the sweep offers the row later). */
77
- private closed;
78
- constructor(kdb: KernelDb, queue: Queue<UsageReportJob>, config: Pick<KernelConfigService, "get">, log: Logger, retry: UsageReportRetry | undefined, events: KernelEvents);
79
- onApplicationShutdown(): void;
49
+ private readonly outbox;
50
+ private readonly subscribers;
51
+ readonly txHost: TransactionHost;
52
+ constructor(kdb: KernelDb, outbox: Pick<OutboxService, "add">, subscribers: Pick<UsageSubscribers, "any">, txHost: TransactionHost);
80
53
  /**
81
- * Enqueues the delivery of one row (job `usage-model-<rowId>`). Without any subscriber nothing is
82
- * enqueued (the sweep marks the row). Never throws: a failure only logs; the sweep catches up.
54
+ * Owes the delivery of every `usage` event among `events` (the events this transaction wrote
55
+ * to the conversation's log), job `usage-<conversationId>-<seq>`.
83
56
  */
84
- enqueue(usageId: string): Promise<void>;
57
+ oweEvents(conversationId: string, events: readonly ConversationEvent[]): Promise<void>;
58
+ /** Owes the delivery of one `model_usage` row (job `usage-model-<rowId>`). */
59
+ oweModelUsage(usageId: string, orgId: string): Promise<void>;
85
60
  }
86
- /** The usage report queue and its `model_usage` producer (no dependency on the extension host). */
61
+ /** The usage report producer (no queue, no dependency on the extension host). */
87
62
  export declare class UsageReportJobsModule {
88
63
  }
@@ -7,152 +7,100 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { getQueueToken } from "@nestjs/bullmq";
11
- import { Inject, Injectable, Module, Optional } from "@nestjs/common";
12
- import { sql } from "kysely";
13
- import { enabledExtensionsQuery } from "../extensions/enabled.js";
14
- import { KernelConfigService } from "../infra/config/kernel-config.service.js";
10
+ import { Inject, Injectable, Module } from "@nestjs/common";
11
+ import { UsageSubscribers, UsageSubscribersModule } from "../extensions/usage-subscribers.js";
15
12
  import { KernelDb } from "../infra/database/kernel-db.js";
16
- import { KernelEvents } from "../infra/events/kernel-events.js";
17
- import { QueueModule } from "../infra/queue/queue.module.js";
18
- import { KERNEL_LOGGER } from "../infra/tokens.js";
13
+ import { TransactionHost } from "../infra/database/transaction-host.js";
14
+ import { Transactional } from "../infra/database/transactional.decorator.js";
15
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
19
16
  /**
20
- * The producer half of `usage.report` delivery for `model_usage` rows, and the queue's ids and job
21
- * options shared with the reporter (`usage-report.ts`). The `ctx.models` broker sits below the
22
- * extension host and must not register a queue there (CLI contexts that build the extensions
23
- * module never connect to Redis eagerly), so it only announces a row (`model_usage.recorded`) and
24
- * {@link ModelUsageJobs} enqueues it here.
17
+ * The producer half of `usage.report` delivery, and the queue's ids shared with the reporter
18
+ * (`usage-report.ts`): a delivery is owed in the transaction that writes the usage it reports
19
+ * (transactional outbox spec §8.1). It registers no queue (the outbox dispatcher adds the job), so
20
+ * the `ctx.models` broker below the extension host and the step writes can use it.
25
21
  */
26
22
  /** Queue of `usage.report` deliveries (no `:` in the name). */
27
23
  export const USAGE_REPORT_QUEUE = "cap-kernel-usage-report";
28
- /** The job name of every delivery. */
24
+ /** The job name of every delivery (its outbox registry entry holds the job options). */
29
25
  export const USAGE_REPORT_JOB = "usage-report";
30
26
  /** One job per `usage` event (no `:`: conversation ids are UUIDs, seq is a number). */
31
27
  export const usageReportJobId = (conversationId, seq) => `usage-${conversationId}-${seq}`;
32
28
  /** One job per `model_usage` row (spec B §5.4; row ids are UUIDs, so no clash with the above). */
33
29
  export const modelUsageJobId = (usageId) => `usage-model-${usageId}`;
34
- /** First attempt plus 5 retries (F11). */
35
- export const USAGE_REPORT_ATTEMPTS = 6;
36
- /** Retry backoff: 2 s, doubling (2, 4, 8, 16, 32 s). */
37
- export const USAGE_REPORT_BACKOFF_MS = 2000;
38
- /** Optional provider: the {@link UsageReportRetry} instead of the defaults above (tests). */
39
- export const USAGE_REPORT_RETRY = Symbol("USAGE_REPORT_RETRY");
40
- /** Failed jobs (given up) are kept for inspection, at most {@link KEEP_FAILED_MAX} of them. */
41
- const KEEP_FAILED_S = 7 * 86_400;
42
- /** Upper bound on kept failed jobs (I-2): a long outage of a billing endpoint cannot grow Redis. */
43
- const KEEP_FAILED_MAX = 100_000;
44
30
  /**
45
- * Upper bound on kept finished jobs (Redis memory). At the default retention it only bites above
46
- * ~1.1 usage events per second sustained for a day; dedupe needs the jobs only for the sweeper overlap.
31
+ * Owes `usage.report` deliveries in the caller's transaction, only while the deployment has a
32
+ * subscriber ({@link UsageSubscribers}): usage written while no plugin subscribes is never
33
+ * reported.
47
34
  */
48
- const KEEP_COMPLETED_MAX = 100_000;
49
- /** The options of one delivery job: its id, the retry policy and the retention of finished jobs. */
50
- export function usageReportJobOpts(jobId, retry, keepHours) {
51
- return {
52
- jobId,
53
- attempts: retry.attempts,
54
- backoff: { type: "exponential", delay: retry.backoffMs },
55
- removeOnComplete: { age: keepHours * 3600, count: KEEP_COMPLETED_MAX },
56
- removeOnFail: { age: KEEP_FAILED_S, count: KEEP_FAILED_MAX },
57
- };
58
- }
59
- /**
60
- * Plugins that subscribe to `usage.report` according to deployment state: an approved permission
61
- * snapshot that declares the `usage.report` hook, for a plugin the deployment still knows: an enabled
62
- * install, or a config-declared plugin with a replica status row that is not `unloaded`. With `orgId`, only plugins
63
- * the organization enabled (org config rows and required bootstrap plugins).
64
- */
65
- export async function queryUsageSubscribers(db, orgId) {
66
- let q = db
67
- .selectFrom("extension_permission_snapshots as s")
68
- .select("s.plugin")
69
- .where(sql `s.permissions -> 'permissions' -> 'hooks' ? 'usage.report'`)
70
- .where((eb) => eb.or([
71
- eb.exists(eb
72
- .selectFrom("extension_installs as i")
73
- .select(sql `1`.as("one"))
74
- .whereRef("i.name", "=", "s.plugin")
75
- .where("i.desired_state", "=", "enabled")),
76
- // config-declared plugins still declared (and not unloaded) on some replica; installs are
77
- // covered by desired_state above (their replica rows linger as `unloaded` when disabled)
78
- eb.exists(eb
79
- .selectFrom("extension_replica_status as r")
80
- .select(sql `1`.as("one"))
81
- .whereRef("r.plugin", "=", "s.plugin")
82
- .where("r.source", "=", "config")
83
- .where("r.state", "<>", "unloaded")),
84
- ]));
85
- if (orgId !== undefined) {
86
- q = q.where("s.plugin", "in", enabledExtensionsQuery(db, orgId));
87
- }
88
- return new Set((await q.execute()).map((r) => r.plugin));
89
- }
90
- /**
91
- * Enqueues the `usage.report` delivery of `model_usage` rows on `model_usage.recorded` (fast path;
92
- * the reporter's sweep re-enqueues rows still unreported, so a lost event or enqueue is caught up).
93
- */
94
- let ModelUsageJobs = class ModelUsageJobs {
35
+ let UsageReportJobs = class UsageReportJobs {
95
36
  kdb;
96
- queue;
97
- config;
98
- log;
99
- retry;
100
- unsubscribe;
101
- /** Set at shutdown: a late event enqueues nothing (the sweep offers the row later). */
102
- closed = false;
103
- constructor(kdb, queue, config, log, retry = {
104
- attempts: USAGE_REPORT_ATTEMPTS,
105
- backoffMs: USAGE_REPORT_BACKOFF_MS,
106
- }, events) {
37
+ outbox;
38
+ subscribers;
39
+ txHost;
40
+ constructor(kdb, outbox, subscribers, txHost) {
107
41
  this.kdb = kdb;
108
- this.queue = queue;
109
- this.config = config;
110
- this.log = log;
111
- this.retry = retry;
112
- this.unsubscribe = events.on("model_usage.recorded", ({ id }) => this.enqueue(id));
113
- }
114
- onApplicationShutdown() {
115
- this.closed = true;
116
- this.unsubscribe();
42
+ this.outbox = outbox;
43
+ this.subscribers = subscribers;
44
+ this.txHost = txHost;
117
45
  }
118
46
  /**
119
- * Enqueues the delivery of one row (job `usage-model-<rowId>`). Without any subscriber nothing is
120
- * enqueued (the sweep marks the row). Never throws: a failure only logs; the sweep catches up.
47
+ * Owes the delivery of every `usage` event among `events` (the events this transaction wrote
48
+ * to the conversation's log), job `usage-<conversationId>-<seq>`.
121
49
  */
122
- async enqueue(usageId) {
123
- if (this.closed)
50
+ async oweEvents(conversationId, events) {
51
+ const usage = events.filter((e) => e.type === "usage");
52
+ if (usage.length === 0 || !(await this.subscribers.any()))
124
53
  return;
125
- try {
126
- if ((await queryUsageSubscribers(this.kdb.db)).size === 0)
127
- return;
128
- await this.queue.add(USAGE_REPORT_JOB, { usageId }, usageReportJobOpts(modelUsageJobId(usageId), this.retry, this.config.get().hooks.usageReportKeepHours));
129
- }
130
- catch (err) {
131
- this.log.warn("cannot enqueue model usage report", { usageId, err });
54
+ const conv = await this.kdb.db
55
+ .selectFrom("conversations")
56
+ .select("org_id")
57
+ .where("id", "=", conversationId)
58
+ .executeTakeFirst();
59
+ for (const e of usage) {
60
+ await this.outbox.add({
61
+ queue: USAGE_REPORT_QUEUE,
62
+ name: USAGE_REPORT_JOB,
63
+ jobId: usageReportJobId(conversationId, e.seq),
64
+ data: { conversationId, seq: e.seq },
65
+ ...(conv !== undefined && { orgId: conv.org_id }),
66
+ });
132
67
  }
133
68
  }
69
+ /** Owes the delivery of one `model_usage` row (job `usage-model-<rowId>`). */
70
+ async oweModelUsage(usageId, orgId) {
71
+ if (!(await this.subscribers.any()))
72
+ return;
73
+ await this.outbox.add({
74
+ queue: USAGE_REPORT_QUEUE,
75
+ name: USAGE_REPORT_JOB,
76
+ jobId: modelUsageJobId(usageId),
77
+ data: { usageId },
78
+ orgId,
79
+ });
80
+ }
134
81
  };
135
- ModelUsageJobs = __decorate([
82
+ __decorate([
83
+ Transactional({ propagation: "mandatory" })
84
+ ], UsageReportJobs.prototype, "oweEvents", null);
85
+ __decorate([
86
+ Transactional({ propagation: "mandatory" })
87
+ ], UsageReportJobs.prototype, "oweModelUsage", null);
88
+ UsageReportJobs = __decorate([
136
89
  Injectable(),
137
90
  __param(0, Inject(KernelDb)),
138
- __param(1, Inject(getQueueToken(USAGE_REPORT_QUEUE))),
139
- __param(2, Inject(KernelConfigService)),
140
- __param(3, Inject(KERNEL_LOGGER)),
141
- __param(4, Optional()),
142
- __param(4, Inject(USAGE_REPORT_RETRY)),
143
- __param(5, Inject(KernelEvents))
144
- ], ModelUsageJobs);
145
- export { ModelUsageJobs };
146
- /** The usage report queue's registration, shared by the producer and the reporter's module. */
147
- const USAGE_REPORT_QUEUE_MODULE = QueueModule.registerQueue(USAGE_REPORT_QUEUE);
148
- /** The usage report queue and its `model_usage` producer (no dependency on the extension host). */
91
+ __param(1, Inject(OutboxService)),
92
+ __param(2, Inject(UsageSubscribers)),
93
+ __param(3, Inject(TransactionHost))
94
+ ], UsageReportJobs);
95
+ export { UsageReportJobs };
96
+ /** The usage report producer (no queue, no dependency on the extension host). */
149
97
  let UsageReportJobsModule = class UsageReportJobsModule {
150
98
  };
151
99
  UsageReportJobsModule = __decorate([
152
100
  Module({
153
- imports: [USAGE_REPORT_QUEUE_MODULE],
154
- providers: [ModelUsageJobs],
155
- exports: [ModelUsageJobs, USAGE_REPORT_QUEUE_MODULE],
101
+ imports: [UsageSubscribersModule],
102
+ providers: [UsageReportJobs],
103
+ exports: [UsageReportJobs],
156
104
  })
157
105
  ], UsageReportJobsModule);
158
106
  export { UsageReportJobsModule };