@capybara-hq/kernel 0.1.0-rc.24 → 0.1.0-rc.26

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 (198) hide show
  1. package/dist/admin/admin.module.js +2 -0
  2. package/dist/admin/health.service.d.ts +2 -2
  3. package/dist/admin/health.service.js +6 -2
  4. package/dist/admin/outbox-dead.service.d.ts +7 -3
  5. package/dist/admin/outbox-dead.service.js +17 -6
  6. package/dist/admin/overview.service.d.ts +6 -6
  7. package/dist/admin/overview.service.js +7 -7
  8. package/dist/agents/agents.module.js +3 -0
  9. package/dist/agents/agents.service.d.ts +6 -1
  10. package/dist/agents/agents.service.js +11 -2
  11. package/dist/background/wake-rules.d.ts +5 -0
  12. package/dist/background/wake-rules.js +16 -5
  13. package/dist/catalog/catalog-import.module.js +0 -4
  14. package/dist/catalog/catalog-scope.service.d.ts +63 -26
  15. package/dist/catalog/catalog-scope.service.js +146 -71
  16. package/dist/catalog/catalog.module.js +4 -3
  17. package/dist/catalog/catalog.service.d.ts +11 -4
  18. package/dist/catalog/catalog.service.js +21 -5
  19. package/dist/catalog/cli.d.ts +4 -3
  20. package/dist/catalog/cli.js +8 -13
  21. package/dist/catalog/job-ids.d.ts +8 -20
  22. package/dist/catalog/job-ids.js +11 -16
  23. package/dist/catalog/marketplaces.service.d.ts +1 -1
  24. package/dist/catalog/org-plugin-uploads.d.ts +11 -4
  25. package/dist/catalog/org-plugin-uploads.js +21 -6
  26. package/dist/catalog/owned-items.d.ts +6 -13
  27. package/dist/catalog/owned-items.js +11 -29
  28. package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
  29. package/dist/catalog/owned-plugin-cleanup.js +12 -66
  30. package/dist/catalog/platform-components.d.ts +56 -34
  31. package/dist/catalog/platform-components.js +68 -78
  32. package/dist/catalog/plugin-import.d.ts +28 -25
  33. package/dist/catalog/plugin-import.js +90 -76
  34. package/dist/catalog/plugin-upgrade.d.ts +4 -4
  35. package/dist/catalog/plugin-upgrade.js +7 -19
  36. package/dist/catalog/scope-jobs.d.ts +59 -0
  37. package/dist/catalog/scope-jobs.js +65 -0
  38. package/dist/catalog/scope-sync-workers.d.ts +22 -0
  39. package/dist/catalog/scope-sync-workers.js +58 -2
  40. package/dist/catalog/scope-sync.d.ts +40 -65
  41. package/dist/catalog/scope-sync.js +114 -122
  42. package/dist/channels/channels.module.d.ts +9 -8
  43. package/dist/channels/channels.module.js +11 -10
  44. package/dist/channels/connections.service.d.ts +3 -3
  45. package/dist/channels/connections.service.js +6 -6
  46. package/dist/channels/deliver-job.d.ts +10 -3
  47. package/dist/channels/deliver-job.js +7 -3
  48. package/dist/channels/inbound.js +2 -2
  49. package/dist/channels/jobs.d.ts +11 -4
  50. package/dist/channels/jobs.js +18 -3
  51. package/dist/channels/outbound.d.ts +5 -2
  52. package/dist/channels/outbound.js +9 -2
  53. package/dist/channels/threads.d.ts +4 -3
  54. package/dist/channels/threads.js +4 -3
  55. package/dist/conversations/conversations.controller.d.ts +4 -2
  56. package/dist/conversations/conversations.controller.js +8 -4
  57. package/dist/conversations/conversations.service.d.ts +54 -9
  58. package/dist/conversations/conversations.service.js +94 -25
  59. package/dist/conversations/events.d.ts +27 -0
  60. package/dist/conversations/events.js +30 -1
  61. package/dist/conversations/source.d.ts +10 -0
  62. package/dist/conversations/source.js +17 -0
  63. package/dist/conversations/tree.d.ts +10 -0
  64. package/dist/conversations/tree.js +35 -0
  65. package/dist/db/kernel-schema.d.ts +19 -15
  66. package/dist/devices/devices.service.d.ts +25 -14
  67. package/dist/devices/devices.service.js +36 -30
  68. package/dist/evals/evals.module.js +5 -1
  69. package/dist/evals/jobs.d.ts +14 -6
  70. package/dist/evals/jobs.js +21 -19
  71. package/dist/evals/run-replay.d.ts +13 -2
  72. package/dist/evals/run-replay.js +28 -1
  73. package/dist/evals/runs.service.d.ts +5 -6
  74. package/dist/evals/runs.service.js +11 -33
  75. package/dist/evals/snapshot.d.ts +7 -1
  76. package/dist/evals/snapshot.js +2 -1
  77. package/dist/extensions/hooks.d.ts +22 -3
  78. package/dist/extensions/hooks.js +15 -1
  79. package/dist/extensions/lifecycle-hooks.d.ts +99 -0
  80. package/dist/extensions/lifecycle-hooks.js +273 -0
  81. package/dist/extensions/org-config.d.ts +13 -1
  82. package/dist/extensions/org-config.js +19 -2
  83. package/dist/extensions/permission-view.d.ts +9 -5
  84. package/dist/extensions/permission-view.js +9 -4
  85. package/dist/extensions/point-defaults.d.ts +28 -1
  86. package/dist/extensions/point-defaults.js +44 -13
  87. package/dist/inbox/inbox.service.d.ts +6 -20
  88. package/dist/inbox/inbox.service.js +0 -3
  89. package/dist/inbox/org-notices.d.ts +1 -1
  90. package/dist/inbox/platform-inbox.service.d.ts +3 -32
  91. package/dist/inbox/platform-inbox.service.js +5 -54
  92. package/dist/infra/outbox/outbox.service.d.ts +6 -0
  93. package/dist/infra/outbox/outbox.service.js +51 -17
  94. package/dist/infra/outbox/registry.d.ts +176 -0
  95. package/dist/infra/outbox/registry.js +183 -0
  96. package/dist/infra/queue/queue-names.d.ts +1 -1
  97. package/dist/infra/queue/queue-names.js +1 -0
  98. package/dist/local-devices/device-policy.service.d.ts +131 -26
  99. package/dist/local-devices/device-policy.service.js +341 -145
  100. package/dist/local-devices/machines.d.ts +17 -2
  101. package/dist/local-devices/machines.js +66 -5
  102. package/dist/local-devices/policy-op-jobs.d.ts +15 -0
  103. package/dist/local-devices/policy-op-jobs.js +12 -0
  104. package/dist/local-devices/policy-summary.d.ts +36 -3
  105. package/dist/local-devices/policy-summary.js +47 -1
  106. package/dist/orgs/directory-sync.js +1 -1
  107. package/dist/orgs/members.service.js +1 -1
  108. package/dist/orgs/offboarding.d.ts +50 -57
  109. package/dist/orgs/offboarding.js +117 -162
  110. package/dist/orgs/orgs.module.js +3 -0
  111. package/dist/orgs/orgs.service.d.ts +11 -5
  112. package/dist/orgs/orgs.service.js +19 -7
  113. package/dist/overrides.d.ts +7 -0
  114. package/dist/overrides.js +4 -0
  115. package/dist/platform/ops/conversations.ops.js +1 -1
  116. package/dist/platform/ops/inbox.ops.js +4 -20
  117. package/dist/platform/ops/platform.ops.d.ts +1 -0
  118. package/dist/platform/ops/platform.ops.js +6 -3
  119. package/dist/roles/worker-role.d.ts +10 -5
  120. package/dist/roles/worker-role.js +19 -9
  121. package/dist/roles/worker.module.js +2 -4
  122. package/dist/runtime/context/build.d.ts +3 -0
  123. package/dist/runtime/context/build.js +15 -0
  124. package/dist/runtime/context/plugin-context.d.ts +20 -0
  125. package/dist/runtime/context/plugin-context.js +30 -0
  126. package/dist/runtime/langgraph-loop.js +4 -0
  127. package/dist/runtime/loop.d.ts +8 -0
  128. package/dist/runtime/maintenance.d.ts +9 -4
  129. package/dist/runtime/maintenance.js +52 -13
  130. package/dist/runtime/model-call.d.ts +6 -1
  131. package/dist/runtime/model-call.js +1 -0
  132. package/dist/runtime/plugin-context.service.d.ts +75 -0
  133. package/dist/runtime/plugin-context.service.js +220 -0
  134. package/dist/runtime/run-context.service.d.ts +7 -3
  135. package/dist/runtime/run-context.service.js +12 -2
  136. package/dist/runtime/run-control.d.ts +6 -1
  137. package/dist/runtime/run-control.js +13 -0
  138. package/dist/runtime/run-finish.d.ts +2 -2
  139. package/dist/runtime/run-finish.js +11 -6
  140. package/dist/runtime/run-jobs.d.ts +21 -4
  141. package/dist/runtime/run-jobs.js +29 -6
  142. package/dist/runtime/run-orphan-check.job.d.ts +14 -11
  143. package/dist/runtime/run-orphan-check.job.js +16 -11
  144. package/dist/runtime/run-queue.d.ts +29 -24
  145. package/dist/runtime/run-queue.js +118 -57
  146. package/dist/runtime/run-status.d.ts +12 -0
  147. package/dist/runtime/run-status.js +21 -0
  148. package/dist/runtime/run-worker.d.ts +32 -3
  149. package/dist/runtime/run-worker.js +143 -21
  150. package/dist/runtime/runtime.module.js +2 -0
  151. package/dist/runtime/step-log.d.ts +6 -5
  152. package/dist/runtime/step-log.js +6 -5
  153. package/dist/runtime/subagents/dispatch.js +1 -0
  154. package/dist/runtime/subagents/skill-fork.js +3 -0
  155. package/dist/runtime/usage-report.d.ts +3 -1
  156. package/dist/runtime/usage-report.js +4 -2
  157. package/dist/runtime/waits.d.ts +16 -6
  158. package/dist/runtime/waits.js +37 -15
  159. package/dist/sandbox/jobs.d.ts +20 -10
  160. package/dist/sandbox/jobs.js +23 -17
  161. package/dist/schedules/fire.d.ts +25 -4
  162. package/dist/schedules/fire.js +87 -13
  163. package/dist/schedules/job-ids.d.ts +66 -17
  164. package/dist/schedules/job-ids.js +81 -13
  165. package/dist/schedules/run-tracker.js +2 -1
  166. package/dist/schedules/scan.job.d.ts +16 -35
  167. package/dist/schedules/scan.job.js +38 -147
  168. package/dist/schedules/scheduled-tasks.service.d.ts +6 -6
  169. package/dist/schedules/scheduled-tasks.service.js +17 -14
  170. package/dist/schedules/schedules.module.js +0 -4
  171. package/dist/skills/catalog.d.ts +7 -3
  172. package/dist/skills/catalog.js +12 -2
  173. package/dist/skills/changes.controller.d.ts +1 -1
  174. package/dist/skills/changes.controller.js +1 -1
  175. package/dist/tools/approvals.service.d.ts +111 -23
  176. package/dist/tools/approvals.service.js +326 -77
  177. package/dist/tools/builtins/sandbox/task-output.js +21 -6
  178. package/dist/tools/device-op-resolver.d.ts +40 -0
  179. package/dist/tools/device-op-resolver.js +121 -0
  180. package/dist/tools/network-asks.d.ts +1 -1
  181. package/dist/tools/network-asks.js +4 -3
  182. package/dist/tools/pipeline.d.ts +11 -2
  183. package/dist/tools/pipeline.js +5 -0
  184. package/dist/tools/questions.service.d.ts +15 -4
  185. package/dist/tools/questions.service.js +21 -19
  186. package/dist/tools/registry.d.ts +8 -1
  187. package/dist/tools/registry.js +9 -0
  188. package/dist/tools/tools.module.js +4 -0
  189. package/package.json +3 -3
  190. package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
  191. package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
  192. package/src/migrations/0123_pgvector.sql +27 -0
  193. package/src/migrations/0124_conversation_source.sql +36 -0
  194. package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
  195. package/dist/catalog/platform-migration.d.ts +0 -115
  196. package/dist/catalog/platform-migration.js +0 -583
  197. package/dist/infra/queue/add-job-once.d.ts +0 -14
  198. package/dist/infra/queue/add-job-once.js +0 -16
@@ -8,31 +8,22 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { randomUUID } from "node:crypto";
11
- import { getQueueToken } from "@nestjs/bullmq";
12
11
  import { Inject, Injectable } from "@nestjs/common";
13
- import { sql } from "kysely";
14
12
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
15
- import { afterCommit } from "../infra/database/after-commit.js";
16
13
  import { KernelDb } from "../infra/database/kernel-db.js";
17
14
  import { TransactionHost } from "../infra/database/transaction-host.js";
18
15
  import { Transactional } from "../infra/database/transactional.decorator.js";
19
16
  import { LockService } from "../infra/lock/lock.service.js";
17
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
20
18
  import { LockedCron } from "../infra/schedule/locked-cron.decorator.js";
21
19
  import { KERNEL_LOGGER } from "../infra/tokens.js";
22
- import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
23
20
  import { readScheduleSettings } from "./effective-settings.js";
24
- import { SCHED_SCAN_JOB, schedFireJob } from "./job-ids.js";
21
+ import { oweSchedFire, oweSchedSettleUnfired, SCHED_SCAN_JOB } from "./job-ids.js";
25
22
  import { pauseTaskIn } from "./pause.js";
26
- import { lockTaskRow, ScheduledTaskRunTracker } from "./run-tracker.js";
27
23
  import { scanStep } from "./schedule-math.js";
28
24
  import { ScheduledTaskStore } from "./store.js";
29
25
  /** Due tasks per scan; what is left waits for the next minute. */
30
26
  const MAX_TASKS = 2000;
31
- /** A pending task run never fired this long after its tick: its fire job is enqueued again. */
32
- const REPAIR_AFTER_MS = 10 * 60_000;
33
- /** ... and this long after its tick it is given up (`skipped`, `missed`). */
34
- const GIVE_UP_AFTER_MS = 60 * 60_000;
35
- const REPAIR_BATCH = 100;
36
27
  /**
37
28
  * The schedule math could not evaluate a due task's stored trigger (its transaction rolled back);
38
29
  * the scan pauses it. Only the math raises it: a database error fails the scan pass instead (the
@@ -51,32 +42,28 @@ class TaskScanFailed extends Error {
51
42
  * same task): the ticks missed outside the org's `catchUpWindowMinutes` become one `skipped` run
52
43
  * (`missed`, at the first missed tick), the latest tick in the window gets its run (the unique
53
44
  * `(task_id, scheduled_for)` key makes a second insert a no-op), `next_run_at` moves to the next
54
- * tick plus the task's jitter (or a `once` task ends, or one past its `expires_at`), and after the
55
- * commit `sched-fire-<taskRunId>` is enqueued. A task whose stored trigger the schedule math cannot
56
- * evaluate is paused (`invalid_schedule`) instead of failing the scan; a database error fails the
57
- * pass (the next minute retries) and pauses nothing.
58
- *
59
- * Then the repair: a run still `pending` and unfired 10 minutes after its tick has its fire job
60
- * enqueued again, and is given up (`skipped`, `missed`) after an hour; a run fired an hour ago and
61
- * still `pending` (its start failed for good) goes through the run tracker's check, which links,
62
- * settles or releases it, so a `once` task's last run never stays pending.
45
+ * tick plus the task's jitter (or a `once` task ends, or one past its `expires_at`), and the same
46
+ * transaction owes `sched-fire-<taskRunId>` (transactional outbox spec §8.1), so a written tick is
47
+ * never left unfired and the scan needs no repair of lost fire jobs (a fire delivered too late
48
+ * marks its tick missed itself, `ScheduledTaskFirer`), and the run's unfired settle check (a fire
49
+ * job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
50
+ * trigger the schedule math cannot evaluate is paused (`invalid_schedule`) instead of failing the
51
+ * scan; a database error fails the pass (the next minute retries) and pauses nothing.
63
52
  */
64
53
  let ScheduledTaskScanJob = class ScheduledTaskScanJob {
65
54
  kdb;
66
55
  txHost;
67
56
  config;
68
57
  store;
69
- tracker;
70
- maintenance;
58
+ outbox;
71
59
  lockService;
72
60
  log;
73
- constructor(kdb, txHost, config, store, tracker, maintenance, lockService, log) {
61
+ constructor(kdb, txHost, config, store, outbox, lockService, log) {
74
62
  this.kdb = kdb;
75
63
  this.txHost = txHost;
76
64
  this.config = config;
77
65
  this.store = store;
78
- this.tracker = tracker;
79
- this.maintenance = maintenance;
66
+ this.outbox = outbox;
80
67
  this.lockService = lockService;
81
68
  this.log = log;
82
69
  }
@@ -86,41 +73,32 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
86
73
  async scan() {
87
74
  await this.scanAt(new Date());
88
75
  }
89
- /**
90
- * One scan as of `now` (tests pass their own); returns how many due tasks it handled. The repair
91
- * runs even when the scan loop failed (its error is rethrown afterwards). `repairBatch`: how many
92
- * rows each repair step takes (tests pass a small one).
93
- */
94
- async scanAt(now, repairBatch = REPAIR_BATCH) {
76
+ /** One scan as of `now` (tests pass their own); returns how many due tasks it handled. */
77
+ async scanAt(now) {
95
78
  const settings = new Map();
96
79
  let total = 0;
97
- try {
98
- for (; total < MAX_TASKS; total++) {
99
- try {
100
- if (!(await this.scanOne(now, settings)))
101
- break;
102
- }
103
- catch (err) {
104
- if (!(err instanceof TaskScanFailed))
105
- throw err;
106
- // one task that cannot be advanced must not hold the others back every minute
107
- this.log.error("scheduled task cannot be advanced; paused", {
108
- taskId: err.taskId,
109
- err: err.cause,
110
- });
111
- await this.quarantine(err.taskId);
112
- }
80
+ for (; total < MAX_TASKS; total++) {
81
+ try {
82
+ if (!(await this.scanOne(now, settings)))
83
+ break;
84
+ }
85
+ catch (err) {
86
+ if (!(err instanceof TaskScanFailed))
87
+ throw err;
88
+ // one task that cannot be advanced must not hold the others back every minute
89
+ this.log.error("scheduled task cannot be advanced; paused", {
90
+ taskId: err.taskId,
91
+ err: err.cause,
92
+ });
93
+ await this.quarantine(err.taskId);
113
94
  }
114
- }
115
- finally {
116
- await this.repair(now, repairBatch);
117
95
  }
118
96
  return total;
119
97
  }
120
98
  /**
121
- * One due task in its own transaction (a task whose trigger cannot be evaluated rolls back
122
- * alone and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take different tasks. False
123
- * when no task is due.
99
+ * One due task in its own transaction (a task whose trigger cannot be evaluated rolls back alone
100
+ * and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take
101
+ * different tasks. False when no task is due.
124
102
  */
125
103
  async scanOne(now, settings) {
126
104
  const t = this.db;
@@ -134,8 +112,8 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
134
112
  }
135
113
  const fired = await this.advance(t, task, now, s.catchUpWindowMinutes);
136
114
  if (fired !== null) {
137
- const id = fired;
138
- afterCommit(() => this.enqueue([id]));
115
+ await oweSchedFire(this.outbox, fired.id, task.orgId);
116
+ await oweSchedSettleUnfired(this.outbox, fired.id, task.orgId, fired.scheduledFor, s.catchUpWindowMinutes);
139
117
  }
140
118
  return true;
141
119
  }
@@ -143,7 +121,7 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
143
121
  async quarantine(taskId) {
144
122
  await pauseTaskIn(this.db, taskId, "invalid_schedule", null);
145
123
  }
146
- /** One due task (see the class); returns the id of the run to fire, if any. */
124
+ /** One due task (see the class); returns the run to fire, if any. */
147
125
  async advance(t, task, now, catchUpWindowMinutes) {
148
126
  // the schedule math alone may mark the task broken; database errors fail this pass only
149
127
  let step;
@@ -178,7 +156,7 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
178
156
  scheduledFor: step.fire,
179
157
  trial: false,
180
158
  });
181
- fired = run?.id ?? null;
159
+ fired = run ?? null;
182
160
  }
183
161
  const patch = step.end === "done"
184
162
  ? { status: "ended", nextRunAt: null }
@@ -188,86 +166,6 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
188
166
  await this.store.updateTask(t, task.id, patch);
189
167
  return fired;
190
168
  }
191
- /** Enqueues the fire jobs (after the commit; a failure is repaired by a later scan). */
192
- async enqueue(ids) {
193
- for (const id of ids) {
194
- const job = schedFireJob(id);
195
- try {
196
- await this.maintenance.add(job.name, job.data, job.opts);
197
- }
198
- catch (err) {
199
- this.log.warn("enqueueing a scheduled task run failed", { taskRunId: id, err });
200
- }
201
- }
202
- }
203
- /** Lost fire jobs (see the class). */
204
- async repair(now, batch) {
205
- const stale = await this.db
206
- .selectFrom("scheduled_task_runs")
207
- .select(["id", "scheduled_for"])
208
- .where("status", "=", "pending")
209
- .where("fired_at", "is", null)
210
- .where("scheduled_for", "<", new Date(now.getTime() - REPAIR_AFTER_MS))
211
- .orderBy("scheduled_for")
212
- .limit(batch)
213
- .execute();
214
- const retry = [];
215
- const lost = [];
216
- for (const r of stale) {
217
- const at = r.scheduled_for instanceof Date ? r.scheduled_for : new Date(r.scheduled_for);
218
- (now.getTime() - at.getTime() > GIVE_UP_AFTER_MS ? lost : retry).push(r.id);
219
- }
220
- for (const id of lost)
221
- await this.giveUp(id);
222
- await this.enqueue(retry);
223
- // the least recently rechecked first, so runs still in flight do not starve the others
224
- const started = await this.db
225
- .selectFrom("scheduled_task_runs")
226
- .select("id")
227
- .where("status", "=", "pending")
228
- .where("fired_at", "<", new Date(now.getTime() - GIVE_UP_AFTER_MS))
229
- .orderBy(sql `rechecked_at NULLS FIRST`)
230
- .orderBy("scheduled_for")
231
- .limit(batch)
232
- .execute();
233
- for (const r of started)
234
- await this.recheck(r.id, now);
235
- }
236
- /** A run never fired an hour after its tick: given up, task row first (the one lock order). */
237
- async giveUp(taskRunId) {
238
- const owner = await this.db
239
- .selectFrom("scheduled_task_runs")
240
- .select("task_id")
241
- .where("id", "=", taskRunId)
242
- .executeTakeFirst();
243
- if (!owner)
244
- return;
245
- await lockTaskRow(this.db, owner.task_id);
246
- await this.db
247
- .updateTable("scheduled_task_runs")
248
- .set({ status: "skipped", skip_reason: "missed", error: "The run could not be started" })
249
- .where("id", "=", taskRunId)
250
- .where("status", "=", "pending")
251
- .where("fired_at", "is", null)
252
- .execute();
253
- }
254
- /**
255
- * A started run still pending long after: the tracker links, settles or releases it; one still
256
- * in flight is stamped (`rechecked_at`) and goes to the back of the next rechecks.
257
- */
258
- async recheck(taskRunId, now) {
259
- const run = await this.store.updateRun(this.db, taskRunId, {});
260
- if (run?.status !== "pending")
261
- return;
262
- // inFlightIn takes the task row before the run (the one lock order)
263
- if (await this.tracker.inFlightIn(run, now)) {
264
- await this.db
265
- .updateTable("scheduled_task_runs")
266
- .set({ rechecked_at: now })
267
- .where("id", "=", taskRunId)
268
- .execute();
269
- }
270
- }
271
169
  };
272
170
  __decorate([
273
171
  LockedCron("* * * * *", { name: SCHED_SCAN_JOB })
@@ -278,21 +176,14 @@ __decorate([
278
176
  __decorate([
279
177
  Transactional()
280
178
  ], ScheduledTaskScanJob.prototype, "quarantine", null);
281
- __decorate([
282
- Transactional()
283
- ], ScheduledTaskScanJob.prototype, "giveUp", null);
284
- __decorate([
285
- Transactional()
286
- ], ScheduledTaskScanJob.prototype, "recheck", null);
287
179
  ScheduledTaskScanJob = __decorate([
288
180
  Injectable(),
289
181
  __param(0, Inject(KernelDb)),
290
182
  __param(1, Inject(TransactionHost)),
291
183
  __param(2, Inject(KernelConfigService)),
292
184
  __param(3, Inject(ScheduledTaskStore)),
293
- __param(4, Inject(ScheduledTaskRunTracker)),
294
- __param(5, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME))),
295
- __param(6, Inject(LockService)),
296
- __param(7, Inject(KERNEL_LOGGER))
185
+ __param(4, Inject(OutboxService)),
186
+ __param(5, Inject(LockService)),
187
+ __param(6, Inject(KERNEL_LOGGER))
297
188
  ], ScheduledTaskScanJob);
298
189
  export { ScheduledTaskScanJob };
@@ -1,4 +1,3 @@
1
- import type { Queue } from "bullmq";
2
1
  import { AgentLookup } from "../agents/agent-lookup.js";
3
2
  import type { ModelRef } from "../agents/definition.js";
4
3
  import { ConversationAccess } from "../conversations/access.js";
@@ -7,6 +6,7 @@ import { ExtensionRegistry } from "../extensions/registry.js";
7
6
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
8
7
  import { KernelDb } from "../infra/database/kernel-db.js";
9
8
  import { TransactionHost } from "../infra/database/transaction-host.js";
9
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
10
10
  import { DevicePolicyService } from "../local-devices/device-policy.service.js";
11
11
  import type { KernelLogger } from "../log.js";
12
12
  import { OrgLock } from "../orgs/org-lock.js";
@@ -198,8 +198,8 @@ export declare class ScheduledTasksService {
198
198
  /** A task's model must resolve for the org and declare its effort (as a message's switch). */
199
199
  private readonly providers;
200
200
  private readonly log;
201
- /** "Run now" enqueues the fire job (consumed by the worker role). */
202
- private readonly maintenance;
201
+ /** "Run now" owes the fire job (consumed by the worker role). */
202
+ private readonly outbox;
203
203
  /** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
204
204
  private readonly sandbox;
205
205
  /** Network grants: the task's device's local policy summary (`remoteGrants`). */
@@ -207,8 +207,8 @@ export declare class ScheduledTasksService {
207
207
  constructor(db: KernelDb, txHost: TransactionHost, config: Pick<KernelConfigService, "get">, store: ScheduledTaskStore, orgLock: Pick<OrgLock, "lockAndAuthorize">, agents: Pick<AgentLookup, "get">, access: Pick<ConversationAccess, "isAccessible">, tools: Pick<ToolRegistry, "forAgent">, extensions: Pick<ExtensionRegistry, "list">,
208
208
  /** A task's model must resolve for the org and declare its effort (as a message's switch). */
209
209
  providers: Pick<ProviderRegistry, "resolve">, log: Pick<KernelLogger, "warn">,
210
- /** "Run now" enqueues the fire job (consumed by the worker role). */
211
- maintenance: Pick<Queue, "add">,
210
+ /** "Run now" owes the fire job (consumed by the worker role). */
211
+ outbox: Pick<OutboxService, "add">,
212
212
  /** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
213
213
  sandbox: Pick<SandboxService, "ceilingLimits" | "devicePolicy">,
214
214
  /** Network grants: the task's device's local policy summary (`remoteGrants`). */
@@ -301,7 +301,7 @@ export declare class ScheduledTasksService {
301
301
  /**
302
302
  * "Run now" (spec §6), creator only: a task run for the request time, outside the schedule (no
303
303
  * unique-key conflict with a tick, and `next_run_at` does not move), fired at once through the
304
- * fire job after the commit. `trial`: an attended trial run whose approvals become the task's
304
+ * fire job its transaction owes. `trial`: an attended trial run whose approvals become the task's
305
305
  * grants (§7). Counted in the rate limit like any run. An ended task cannot run; a paused one
306
306
  * can (only its creator gets here: the run is `manual`, which the fire's paused check lets by).
307
307
  */
@@ -9,7 +9,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
9
9
  };
10
10
  import { randomUUID } from "node:crypto";
11
11
  import { CHANNEL_EXTENSION_POINT } from "@capybara-hq/plugin-sdk";
12
- import { getQueueToken } from "@nestjs/bullmq";
13
12
  import { Inject, Injectable } from "@nestjs/common";
14
13
  import { AgentLookup } from "../agents/agent-lookup.js";
15
14
  import { ConversationAccess } from "../conversations/access.js";
@@ -18,16 +17,15 @@ import { ApiError } from "../errors.js";
18
17
  import { ExtensionRegistry } from "../extensions/registry.js";
19
18
  import { audit } from "../identity/audit.js";
20
19
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
21
- import { afterCommit } from "../infra/database/after-commit.js";
22
20
  import { KernelDb } from "../infra/database/kernel-db.js";
23
21
  import { TransactionHost } from "../infra/database/transaction-host.js";
24
22
  import { Transactional } from "../infra/database/transactional.decorator.js";
23
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
25
24
  import { KERNEL_LOGGER } from "../infra/tokens.js";
26
25
  import { DevicePolicyService } from "../local-devices/device-policy.service.js";
27
26
  import { orgAllows } from "../local-devices/network-grants.js";
28
27
  import { OrgLock } from "../orgs/org-lock.js";
29
28
  import { can, roleAtLeast } from "../orgs/roles.js";
30
- import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
31
29
  import { ProviderRegistry } from "../runtime/providers.js";
32
30
  import { SandboxService } from "../sandbox/sandbox.service.js";
33
31
  import { isTimeZone } from "../time-zone.js";
@@ -36,7 +34,7 @@ import { assembleCalendar, CALENDAR_LIMITS, CALENDAR_RANGE_DAYS, CALENDAR_TASK_R
36
34
  import { readScheduleSettings } from "./effective-settings.js";
37
35
  import { grantNotAllowed, scheduleDisabled, scheduleForbidden, scheduleNotFound, targetInvalid, } from "./errors.js";
38
36
  import { grantRefusal, grantRefusalOf, isNetworkGrantKey, isPolicyGrantKey, isTierGrantKey, NETWORK_GRANT_PREFIX, NETWORK_GRANT_SPEC_HASH, networkGrantHost, networkGrantRefusal, TIER_GRANT_SPEC_HASH, tierGrantRefusal, } from "./grant-rules.js";
39
- import { schedFireJob } from "./job-ids.js";
37
+ import { oweSchedFire, oweSchedSettleUnfired } from "./job-ids.js";
40
38
  import { pauseTaskIn } from "./pause.js";
41
39
  import { jitterForGap, minGapMinutes, nextTick, validateTrigger } from "./schedule-math.js";
42
40
  import { ScheduledTaskStore } from "./store.js";
@@ -75,14 +73,14 @@ let ScheduledTasksService = class ScheduledTasksService {
75
73
  extensions;
76
74
  providers;
77
75
  log;
78
- maintenance;
76
+ outbox;
79
77
  sandbox;
80
78
  devicePolicy;
81
79
  constructor(db, txHost, config, store, orgLock, agents, access, tools, extensions,
82
80
  /** A task's model must resolve for the org and declare its effort (as a message's switch). */
83
81
  providers, log,
84
- /** "Run now" enqueues the fire job (consumed by the worker role). */
85
- maintenance,
82
+ /** "Run now" owes the fire job (consumed by the worker role). */
83
+ outbox,
86
84
  /** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
87
85
  sandbox,
88
86
  /** Network grants: the task's device's local policy summary (`remoteGrants`). */
@@ -98,7 +96,7 @@ let ScheduledTasksService = class ScheduledTasksService {
98
96
  this.extensions = extensions;
99
97
  this.providers = providers;
100
98
  this.log = log;
101
- this.maintenance = maintenance;
99
+ this.outbox = outbox;
102
100
  this.sandbox = sandbox;
103
101
  this.devicePolicy = devicePolicy;
104
102
  }
@@ -525,6 +523,8 @@ let ScheduledTasksService = class ScheduledTasksService {
525
523
  disabledTools: d.disabledTools ?? [],
526
524
  subagentTools: d.subagentTools,
527
525
  subagents: d.subagents,
526
+ // no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
527
+ source: "web",
528
528
  });
529
529
  const ctx = {
530
530
  role,
@@ -789,6 +789,8 @@ let ScheduledTasksService = class ScheduledTasksService {
789
789
  disabledTools: d.disabledTools ?? [],
790
790
  subagentTools: d.subagentTools,
791
791
  subagents: d.subagents,
792
+ // no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
793
+ source: "web",
792
794
  });
793
795
  return new Map(tools.map((x) => [
794
796
  x.key,
@@ -820,7 +822,7 @@ let ScheduledTasksService = class ScheduledTasksService {
820
822
  /**
821
823
  * "Run now" (spec §6), creator only: a task run for the request time, outside the schedule (no
822
824
  * unique-key conflict with a tick, and `next_run_at` does not move), fired at once through the
823
- * fire job after the commit. `trial`: an attended trial run whose approvals become the task's
825
+ * fire job its transaction owes. `trial`: an attended trial run whose approvals become the task's
824
826
  * grants (§7). Counted in the rate limit like any run. An ended task cannot run; a paused one
825
827
  * can (only its creator gets here: the run is `manual`, which the fire's paused check lets by).
826
828
  */
@@ -850,10 +852,9 @@ let ScheduledTasksService = class ScheduledTasksService {
850
852
  target: task.id,
851
853
  detail: { taskRunId: run.id, trial: opts.trial },
852
854
  });
853
- const job = schedFireJob(run.id);
854
- afterCommit(async () => {
855
- await this.maintenance.add(job.name, job.data, job.opts);
856
- });
855
+ await oweSchedFire(this.outbox, run.id, orgId);
856
+ // a fire job that fails for good before its claim leaves the run to this check (`removed`)
857
+ await oweSchedSettleUnfired(this.outbox, run.id, orgId, run.scheduledFor, null);
857
858
  return run;
858
859
  }
859
860
  /** The live role of a member of the org; not a member → `schedule.not_found`. */
@@ -1083,6 +1084,8 @@ let ScheduledTasksService = class ScheduledTasksService {
1083
1084
  disabledTools: d.disabledTools ?? [],
1084
1085
  subagentTools: d.subagentTools,
1085
1086
  subagents: d.subagents,
1087
+ // no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
1088
+ source: "web",
1086
1089
  });
1087
1090
  for (const g of toolGrants) {
1088
1091
  const tool = tools.find((x) => x.key === g.toolKey);
@@ -1168,7 +1171,7 @@ ScheduledTasksService = __decorate([
1168
1171
  __param(8, Inject(ExtensionRegistry)),
1169
1172
  __param(9, Inject(ProviderRegistry)),
1170
1173
  __param(10, Inject(KERNEL_LOGGER)),
1171
- __param(11, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME))),
1174
+ __param(11, Inject(OutboxService)),
1172
1175
  __param(12, Inject(SandboxService)),
1173
1176
  __param(13, Inject(DevicePolicyService))
1174
1177
  ], ScheduledTasksService);
@@ -11,10 +11,8 @@ import { Inject, Module } from "@nestjs/common";
11
11
  import { AgentLookupModule } from "../agents/agent-lookup.js";
12
12
  import { ConversationAccessModule } from "../conversations/access.js";
13
13
  import { ExtensionRegistryModule } from "../extensions/registry.js";
14
- import { QueueModule } from "../infra/queue/queue.module.js";
15
14
  import { LocalDevicesModule } from "../local-devices/local-devices.module.js";
16
15
  import { OrgLockModule } from "../orgs/org-lock.js";
17
- import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
18
16
  import { ProvidersModule } from "../runtime/providers.js";
19
17
  import { SandboxModule } from "../sandbox/sandbox.module.js";
20
18
  import { ScheduleToolsPort } from "../tools/builtins/schedules.js";
@@ -53,8 +51,6 @@ SchedulesModule = __decorate([
53
51
  // network grants: the org's effective egress and the task device's policy summary
54
52
  SandboxModule,
55
53
  LocalDevicesModule,
56
- // "run now" enqueues the fire job
57
- QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
58
54
  ],
59
55
  providers: [ScheduledTasksService],
60
56
  exports: [ScheduledTasksService, ScheduledTaskStoreModule],
@@ -156,13 +156,17 @@ export declare class SkillsCatalogService {
156
156
  constructor(db: KernelDb, eventLog: EventLog, log: KernelLogger, txHost: TransactionHost, config: KernelConfigService);
157
157
  /**
158
158
  * Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
159
- * (default the kernel logger).
159
+ * (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
160
+ * appended and the run must not go on.
160
161
  */
161
162
  record(input: EffectiveSkillsInput & {
162
163
  runId: string;
163
164
  }, opts?: {
164
165
  log?: Pick<Logger, "warn" | "error">;
165
- }): Promise<void>;
166
- /** Under the conversation row lock, resolves the skills and appends a changed snapshot. */
166
+ }): Promise<boolean>;
167
+ /**
168
+ * Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
169
+ * (nothing appended) when the run is terminal under the locks.
170
+ */
167
171
  private append;
168
172
  }
@@ -18,6 +18,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
18
18
  import { KERNEL_LOGGER } from "../infra/tokens.js";
19
19
  import { CLAUDE_CODE_TOOL_MAP } from "../runtime/context/claude-tools.js";
20
20
  import { catalogLine, sameSnapshot, } from "../runtime/context/skills.js";
21
+ import { runEndedIn } from "../runtime/run-status.js";
21
22
  import { agentOwnSkillIds, agentSkills, builtinSkillIds, builtinSkillPlugins, } from "./usable.js";
22
23
  /**
23
24
  * The skills in effect for this conversation now, sorted by name (code point order), as
@@ -264,16 +265,25 @@ let SkillsCatalogService = class SkillsCatalogService {
264
265
  }
265
266
  /**
266
267
  * Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
267
- * (default the kernel logger).
268
+ * (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
269
+ * appended and the run must not go on.
268
270
  */
269
271
  async record(input, opts = {}) {
270
272
  const toSeq = await this.append(input, opts.log ?? this.log);
273
+ if (toSeq === "ended")
274
+ return false;
271
275
  if (toSeq !== null)
272
276
  await this.eventLog.notify(input.conversationId, toSeq);
277
+ return true;
273
278
  }
274
- /** Under the conversation row lock, resolves the skills and appends a changed snapshot. */
279
+ /**
280
+ * Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
281
+ * (nothing appended) when the run is terminal under the locks.
282
+ */
275
283
  async append(input, log) {
276
284
  const last = await lockLatestCatalog(this.db.db, input.conversationId);
285
+ if (await runEndedIn(this.db.db, input.runId))
286
+ return "ended";
277
287
  const all = await resolveSkills(this.db.db, input, log);
278
288
  const resolved = forModel(all, input, log);
279
289
  const budget = this.config.get().skills.catalogBudgetChars;
@@ -40,7 +40,7 @@ export declare class InboxController {
40
40
  private readonly inbox;
41
41
  constructor(inbox: InboxService);
42
42
  list(principal: Principal, org: OrgContext): Promise<InboxView>;
43
- /** An org admin dismisses an org notice (`skill_renamed`) -> 204. */
43
+ /** An org admin dismisses an org notice -> 204. */
44
44
  dismissNotice(principal: Principal, org: OrgContext, params: z.infer<typeof NoticeParamsSchema>): Promise<void>;
45
45
  }
46
46
  export {};
@@ -86,7 +86,7 @@ let InboxController = class InboxController {
86
86
  const p = requireUser(principal);
87
87
  return this.inbox.list(org.orgId, p.account.id);
88
88
  }
89
- /** An org admin dismisses an org notice (`skill_renamed`) -> 204. */
89
+ /** An org admin dismisses an org notice -> 204. */
90
90
  async dismissNotice(principal, org, params) {
91
91
  const p = requireUser(principal);
92
92
  await this.inbox.dismissNotice(org.orgId, p.account.id, params.id);