@capybara-hq/kernel 0.1.0-rc.21 → 0.1.0-rc.22

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 (242) hide show
  1. package/dist/agents/admin-agents.d.ts +16 -3
  2. package/dist/agents/admin-agents.js +65 -7
  3. package/dist/agents/agents.schemas.d.ts +24 -6
  4. package/dist/agents/agents.service.d.ts +4 -3
  5. package/dist/agents/agents.service.js +39 -26
  6. package/dist/agents/definition.d.ts +44 -6
  7. package/dist/agents/definition.js +86 -18
  8. package/dist/agents/from-template.d.ts +6 -3
  9. package/dist/agents/from-template.js +57 -44
  10. package/dist/agents/org-templates.js +97 -41
  11. package/dist/agents/templates.d.ts +6 -0
  12. package/dist/agents/templates.js +15 -1
  13. package/dist/catalog/admin-catalog.controller.d.ts +2 -4
  14. package/dist/catalog/admin-catalog.controller.js +2 -6
  15. package/dist/catalog/catalog.module.js +3 -0
  16. package/dist/catalog/claude/report.d.ts +9 -2
  17. package/dist/catalog/claude/report.js +1 -0
  18. package/dist/catalog/claude/translate.js +1 -1
  19. package/dist/catalog/component-promotion.js +1 -2
  20. package/dist/catalog/component-state.d.ts +13 -1
  21. package/dist/catalog/component-state.js +14 -5
  22. package/dist/catalog/marketplaces.service.d.ts +1 -1
  23. package/dist/catalog/marketplaces.service.js +2 -1
  24. package/dist/catalog/org-catalog.controller.d.ts +13 -3
  25. package/dist/catalog/org-catalog.controller.js +26 -4
  26. package/dist/catalog/org-catalog.schemas.d.ts +13 -0
  27. package/dist/catalog/org-catalog.schemas.js +34 -0
  28. package/dist/catalog/org-plugin-uploads.controller.js +0 -1
  29. package/dist/catalog/org-plugin-uploads.d.ts +0 -1
  30. package/dist/catalog/org-plugin-uploads.js +0 -3
  31. package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
  32. package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
  33. package/dist/catalog/platform-components.d.ts +0 -1
  34. package/dist/catalog/platform-components.js +3 -8
  35. package/dist/catalog/plugin-import.d.ts +0 -8
  36. package/dist/catalog/plugin-import.js +4 -35
  37. package/dist/catalog/plugin-install.d.ts +2 -2
  38. package/dist/catalog/plugin-install.js +2 -2
  39. package/dist/catalog/plugin-package.d.ts +7 -0
  40. package/dist/catalog/plugin-package.js +27 -1
  41. package/dist/catalog/plugin-upgrade.d.ts +5 -6
  42. package/dist/catalog/plugin-upgrade.js +5 -23
  43. package/dist/catalog/requests.service.d.ts +66 -10
  44. package/dist/catalog/requests.service.js +196 -15
  45. package/dist/channels/actions.d.ts +2 -0
  46. package/dist/channels/actions.js +19 -4
  47. package/dist/channels/cards.d.ts +16 -2
  48. package/dist/channels/cards.js +40 -7
  49. package/dist/channels/channels.module.js +3 -0
  50. package/dist/channels/outbound.d.ts +22 -1
  51. package/dist/channels/outbound.js +116 -7
  52. package/dist/channels/phrases.d.ts +25 -0
  53. package/dist/channels/phrases.js +19 -0
  54. package/dist/channels/threads.d.ts +12 -0
  55. package/dist/channels/threads.js +34 -0
  56. package/dist/config/schema.d.ts +23 -1
  57. package/dist/config/schema.js +105 -5
  58. package/dist/conversations/conversations.service.d.ts +42 -9
  59. package/dist/conversations/conversations.service.js +57 -25
  60. package/dist/conversations/events.d.ts +106 -11
  61. package/dist/conversations/events.js +121 -18
  62. package/dist/conversations/message-context.d.ts +6 -3
  63. package/dist/conversations/message-context.js +14 -10
  64. package/dist/conversations/queue.d.ts +15 -2
  65. package/dist/conversations/queue.js +22 -3
  66. package/dist/conversations/queue.service.d.ts +12 -6
  67. package/dist/conversations/queue.service.js +36 -13
  68. package/dist/db/kernel-schema.d.ts +105 -3
  69. package/dist/devices/conversation-devices.d.ts +8 -1
  70. package/dist/devices/conversation-devices.js +8 -0
  71. package/dist/devices/devices.controller.d.ts +21 -4
  72. package/dist/devices/devices.controller.js +46 -8
  73. package/dist/devices/devices.schemas.d.ts +9 -1
  74. package/dist/devices/devices.schemas.js +11 -3
  75. package/dist/devices/devices.service.d.ts +176 -4
  76. package/dist/devices/devices.service.js +341 -6
  77. package/dist/devices/dto.d.ts +56 -3
  78. package/dist/devices/dto.js +51 -1
  79. package/dist/devices/public-suffix.d.ts +6 -0
  80. package/dist/devices/public-suffix.js +79 -0
  81. package/dist/errors.d.ts +46 -5
  82. package/dist/errors.js +46 -5
  83. package/dist/evals/reconstruct.d.ts +18 -13
  84. package/dist/evals/reconstruct.js +50 -36
  85. package/dist/evals/recorded-tools.d.ts +7 -7
  86. package/dist/evals/recorded-tools.js +4 -3
  87. package/dist/evals/trajectory.d.ts +6 -1
  88. package/dist/evals/trajectory.js +7 -0
  89. package/dist/extensions/package.js +2 -2
  90. package/dist/http/api.module.js +5 -0
  91. package/dist/inbox/inbox.module.js +2 -1
  92. package/dist/inbox/inbox.service.d.ts +19 -4
  93. package/dist/inbox/inbox.service.js +14 -3
  94. package/dist/infra/config/kernel-config.service.js +11 -0
  95. package/dist/infra/events/kernel-events.d.ts +2 -1
  96. package/dist/local-devices/device-policy.service.d.ts +65 -0
  97. package/dist/local-devices/device-policy.service.js +217 -0
  98. package/dist/local-devices/local-device-gateway.js +4 -1
  99. package/dist/local-devices/local-device-provider.d.ts +40 -3
  100. package/dist/local-devices/local-device-provider.js +109 -7
  101. package/dist/local-devices/local-devices.module.d.ts +2 -1
  102. package/dist/local-devices/local-devices.module.js +19 -3
  103. package/dist/local-devices/machines.d.ts +14 -3
  104. package/dist/local-devices/machines.js +41 -3
  105. package/dist/local-devices/network-grants.d.ts +104 -0
  106. package/dist/local-devices/network-grants.js +288 -0
  107. package/dist/local-devices/policy-summary.d.ts +20 -0
  108. package/dist/local-devices/policy-summary.js +63 -0
  109. package/dist/orgs/orgs.module.js +4 -10
  110. package/dist/orgs/roles.d.ts +1 -1
  111. package/dist/orgs/roles.js +4 -0
  112. package/dist/orgs/settings.controller.js +2 -1
  113. package/dist/orgs/settings.service.d.ts +164 -12
  114. package/dist/orgs/settings.service.js +212 -24
  115. package/dist/platform/exclusions.js +11 -1
  116. package/dist/platform/operation.js +4 -0
  117. package/dist/platform/ops/admin-items.ops.js +1 -1
  118. package/dist/platform/ops/agent-skills.ops.js +2 -2
  119. package/dist/platform/ops/agent-templates.ops.js +2 -2
  120. package/dist/platform/ops/agents.ops.js +11 -15
  121. package/dist/platform/ops/devices.ops.js +3 -1
  122. package/dist/platform/ops/inbox.ops.js +19 -0
  123. package/dist/platform/ops/org-settings.ops.js +14 -5
  124. package/dist/platform/ops/platform.ops.d.ts +9 -1
  125. package/dist/platform/ops/platform.ops.js +34 -12
  126. package/dist/platform/ops/requests.ops.js +3 -2
  127. package/dist/platform/ops/schedules.ops.d.ts +21 -0
  128. package/dist/platform/ops/schedules.ops.js +470 -0
  129. package/dist/platform/ops/skills.ops.js +3 -3
  130. package/dist/platform/platform-ops.module.d.ts +2 -1
  131. package/dist/platform/platform-ops.module.js +5 -0
  132. package/dist/platform/route-schemas.js +3 -0
  133. package/dist/roles/worker.module.js +3 -0
  134. package/dist/runtime/context/build.js +13 -4
  135. package/dist/runtime/context/compaction.js +6 -3
  136. package/dist/runtime/context/device-paths.d.ts +43 -21
  137. package/dist/runtime/context/device-paths.js +104 -21
  138. package/dist/runtime/context/run-context.d.ts +9 -0
  139. package/dist/runtime/context/run-context.js +8 -0
  140. package/dist/runtime/context/skills.d.ts +14 -1
  141. package/dist/runtime/context/skills.js +36 -5
  142. package/dist/runtime/maintenance.d.ts +4 -2
  143. package/dist/runtime/maintenance.js +16 -5
  144. package/dist/runtime/run-finish.d.ts +6 -1
  145. package/dist/runtime/run-finish.js +16 -2
  146. package/dist/runtime/run-queue.js +21 -2
  147. package/dist/runtime/run-worker.js +14 -33
  148. package/dist/runtime/runtime.module.js +3 -0
  149. package/dist/runtime/subagents/definition.d.ts +32 -14
  150. package/dist/runtime/subagents/definition.js +106 -109
  151. package/dist/runtime/subagents/dispatch.js +4 -3
  152. package/dist/runtime/subagents/preamble.d.ts +2 -1
  153. package/dist/runtime/subagents/preamble.js +2 -1
  154. package/dist/runtime/waits.js +14 -4
  155. package/dist/sandbox/sandbox.service.d.ts +120 -10
  156. package/dist/sandbox/sandbox.service.js +335 -28
  157. package/dist/sandbox/tiers.d.ts +86 -0
  158. package/dist/sandbox/tiers.js +96 -0
  159. package/dist/schedules/effective-settings.d.ts +16 -0
  160. package/dist/schedules/effective-settings.js +17 -0
  161. package/dist/schedules/errors.d.ts +8 -0
  162. package/dist/schedules/errors.js +8 -0
  163. package/dist/schedules/fire.d.ts +118 -0
  164. package/dist/schedules/fire.js +625 -0
  165. package/dist/schedules/grant-rules.d.ts +94 -0
  166. package/dist/schedules/grant-rules.js +126 -0
  167. package/dist/schedules/job-ids.d.ts +32 -0
  168. package/dist/schedules/job-ids.js +27 -0
  169. package/dist/schedules/pause.d.ts +9 -0
  170. package/dist/schedules/pause.js +33 -0
  171. package/dist/schedules/run-tracker.d.ts +81 -0
  172. package/dist/schedules/run-tracker.js +333 -0
  173. package/dist/schedules/scan.job.d.ts +64 -0
  174. package/dist/schedules/scan.job.js +303 -0
  175. package/dist/schedules/schedule-math.d.ts +31 -0
  176. package/dist/schedules/schedule-math.js +223 -0
  177. package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
  178. package/dist/schedules/scheduled-tasks.service.js +1193 -0
  179. package/dist/schedules/schedules.controller.d.ts +68 -0
  180. package/dist/schedules/schedules.controller.js +242 -0
  181. package/dist/schedules/schedules.module.d.ts +16 -0
  182. package/dist/schedules/schedules.module.js +65 -0
  183. package/dist/schedules/schedules.schemas.d.ts +316 -0
  184. package/dist/schedules/schedules.schemas.js +186 -0
  185. package/dist/schedules/store.d.ts +81 -0
  186. package/dist/schedules/store.js +390 -0
  187. package/dist/schedules/store.module.d.ts +6 -0
  188. package/dist/schedules/store.module.js +21 -0
  189. package/dist/schedules/types.d.ts +108 -0
  190. package/dist/schedules/types.js +1 -0
  191. package/dist/schedules/unattended.d.ts +117 -0
  192. package/dist/schedules/unattended.js +228 -0
  193. package/dist/setup/oauth-token-probe.js +8 -4
  194. package/dist/skills/agent-skills.service.d.ts +4 -8
  195. package/dist/skills/agent-skills.service.js +18 -53
  196. package/dist/skills/catalog.d.ts +65 -62
  197. package/dist/skills/catalog.js +109 -206
  198. package/dist/skills/conversation-catalog.d.ts +3 -3
  199. package/dist/skills/conversation-catalog.js +5 -12
  200. package/dist/skills/invoke.d.ts +2 -2
  201. package/dist/skills/skills.controller.d.ts +3 -1
  202. package/dist/skills/skills.service.d.ts +38 -37
  203. package/dist/skills/skills.service.js +168 -107
  204. package/dist/skills/usable.d.ts +57 -8
  205. package/dist/skills/usable.js +172 -38
  206. package/dist/tools/approvals.controller.d.ts +4 -2
  207. package/dist/tools/approvals.controller.js +17 -2
  208. package/dist/tools/approvals.schemas.d.ts +1 -0
  209. package/dist/tools/approvals.schemas.js +3 -1
  210. package/dist/tools/approvals.service.d.ts +182 -7
  211. package/dist/tools/approvals.service.js +597 -13
  212. package/dist/tools/builtin-names.d.ts +25 -4
  213. package/dist/tools/builtin-names.js +45 -4
  214. package/dist/tools/builtins/builtin-tools.d.ts +8 -0
  215. package/dist/tools/builtins/builtin-tools.js +23 -2
  216. package/dist/tools/builtins/sandbox/bash.js +16 -7
  217. package/dist/tools/builtins/sandbox/common.d.ts +1 -1
  218. package/dist/tools/builtins/sandbox/common.js +33 -10
  219. package/dist/tools/builtins/sandbox/edit.js +7 -3
  220. package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
  221. package/dist/tools/builtins/sandbox/file-state.js +33 -3
  222. package/dist/tools/builtins/sandbox/write.js +8 -4
  223. package/dist/tools/builtins/schedules.d.ts +107 -0
  224. package/dist/tools/builtins/schedules.js +789 -0
  225. package/dist/tools/elicitation.d.ts +2 -0
  226. package/dist/tools/elicitation.js +1 -1
  227. package/dist/tools/fixed-ask.d.ts +7 -0
  228. package/dist/tools/fixed-ask.js +9 -0
  229. package/dist/tools/network-asks.d.ts +109 -0
  230. package/dist/tools/network-asks.js +221 -0
  231. package/dist/tools/pipeline.d.ts +53 -6
  232. package/dist/tools/pipeline.js +202 -13
  233. package/dist/tools/policy.d.ts +21 -8
  234. package/dist/tools/policy.js +63 -9
  235. package/dist/tools/registry.d.ts +10 -1
  236. package/dist/tools/tools.module.js +12 -0
  237. package/package.json +2 -2
  238. package/src/migrations/0113_scheduled_tasks.sql +108 -0
  239. package/src/migrations/0114_network_asks.sql +23 -0
  240. package/src/migrations/0115_network_requests.sql +28 -0
  241. package/src/migrations/0116_skills_default_on.sql +37 -0
  242. package/src/migrations/0117_sandbox_tiers.sql +11 -0
@@ -0,0 +1,333 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Inject, Injectable, Module } from "@nestjs/common";
11
+ import { sql } from "kysely";
12
+ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
13
+ import { KernelDb } from "../infra/database/kernel-db.js";
14
+ import { TransactionHost } from "../infra/database/transaction-host.js";
15
+ import { Transactional } from "../infra/database/transactional.decorator.js";
16
+ import { readScheduleSettings } from "./effective-settings.js";
17
+ import { pauseTaskIn } from "./pause.js";
18
+ /** The kernel run statuses that are terminal (`runtime/run-finish.ts`, not imported: a cycle). */
19
+ const RUN_TERMINAL = new Set(["completed", "failed", "aborted"]);
20
+ /**
21
+ * How long a task run that was started (its message created or queued) may show no trace of its
22
+ * message before the overlap check releases it: the fire job writes `fired_at` before it creates
23
+ * the conversation or queues the item, in a transaction of its own.
24
+ */
25
+ export const START_GRACE_MS = 5 * 60_000;
26
+ /**
27
+ * Follows a task run's kernel run to its end (scheduled tasks spec §6). {@link onRunFinished} runs
28
+ * in the transaction that writes a run's terminal state (`RunFinish.finishIn`, the one writer of
29
+ * terminal states), so a task run never misses its run's end: a run aborted by a person counts as
30
+ * completed; a failed one counts against the task (`consecutive_failures`, paused at the org's
31
+ * `pauseAfterFailures`), a completed one resets the count. Trial runs do not count.
32
+ * {@link inFlightIn} is the overlap check's view of the task's previous run: it settles a run whose
33
+ * end was missed and releases one whose queued message is gone.
34
+ */
35
+ let ScheduledTaskRunTracker = class ScheduledTaskRunTracker {
36
+ kdb;
37
+ txHost;
38
+ config;
39
+ constructor(kdb, txHost, config) {
40
+ this.kdb = kdb;
41
+ this.txHost = txHost;
42
+ this.config = config;
43
+ }
44
+ get db() {
45
+ return this.kdb.db;
46
+ }
47
+ /**
48
+ * The kernel run `runId` ended with `outcome`: the task run it was started for (the run its
49
+ * message triggered, `scheduled_task_runs.run_id`) takes the outcome. A sub-agent or wake run
50
+ * that inherited the marker is not the task run's own and changes nothing.
51
+ */
52
+ async onRunFinished(runId, outcome, error) {
53
+ const run = await this.db
54
+ .selectFrom("runs")
55
+ .select("scheduled_task_run_id")
56
+ .where("id", "=", runId)
57
+ .executeTakeFirst();
58
+ const taskRunId = run?.scheduled_task_run_id;
59
+ if (taskRunId == null)
60
+ return;
61
+ const owner = await this.db
62
+ .selectFrom("scheduled_task_runs")
63
+ .select("task_id")
64
+ .where("id", "=", taskRunId)
65
+ .executeTakeFirst();
66
+ if (!owner)
67
+ return;
68
+ // lock order task row → task run, as the fire job's claim takes them (no lock cycle)
69
+ await lockTaskRow(this.db, owner.task_id);
70
+ const tr = await this.db
71
+ .selectFrom("scheduled_task_runs")
72
+ .select(["id", "task_id", "org_id", "trial", "status", "run_id"])
73
+ .where("id", "=", taskRunId)
74
+ .forUpdate()
75
+ .executeTakeFirst();
76
+ if (!tr || tr.run_id !== runId || tr.status !== "running")
77
+ return;
78
+ await this.settle(tr, outcome, error);
79
+ }
80
+ /**
81
+ * Whether `prev`, a run of the task still `pending` or `running`, is really in flight (the
82
+ * overlap check, inside the fire's transaction). Settled on the way: a running one whose kernel
83
+ * run already ended takes its outcome; a started one linked to its run now when the run exists;
84
+ * a started one whose message another run consumed (`consumed_seq`, no run of its own) ends
85
+ * `completed`; a started one with no queued item, no message and no run after {@link START_GRACE_MS} (its
86
+ * queued item was removed or the queue cleared without the queue's own hook) becomes `skipped`
87
+ * (`removed`). The marker alone decides what is the task's message, never a client message id.
88
+ */
89
+ async inFlightIn(prev, now = new Date()) {
90
+ const t = this.db;
91
+ await lockTaskRow(t, prev.taskId);
92
+ const tr = await t
93
+ .selectFrom("scheduled_task_runs")
94
+ .selectAll()
95
+ .where("id", "=", prev.id)
96
+ .forUpdate()
97
+ .executeTakeFirst();
98
+ if (!tr || (tr.status !== "pending" && tr.status !== "running"))
99
+ return false;
100
+ if (tr.status === "running") {
101
+ if (tr.run_id === null)
102
+ return true;
103
+ const run = await t
104
+ .selectFrom("runs")
105
+ .select("status")
106
+ .where("id", "=", tr.run_id)
107
+ .executeTakeFirst();
108
+ if (!run) {
109
+ await this.settle(tr, "failed", "The run was lost");
110
+ return false;
111
+ }
112
+ if (!RUN_TERMINAL.has(run.status))
113
+ return true;
114
+ await this.settle(tr, run.status);
115
+ return false;
116
+ }
117
+ // not started yet: its fire job is still to come (the scanner repairs a lost one)
118
+ if (tr.fired_at === null)
119
+ return true;
120
+ // only a run with a target conversation is ever queued (`conversation`, `chat`): no scan of
121
+ // every queue's markers for the others
122
+ const queued = tr.conversation_id === null
123
+ ? undefined
124
+ : await t
125
+ .selectFrom("conversation_queue")
126
+ .select("id")
127
+ .where("conversation_id", "=", tr.conversation_id)
128
+ .where(sql `schedule ->> 'taskRunId'`, "=", tr.id)
129
+ .limit(1)
130
+ .executeTakeFirst();
131
+ if (queued)
132
+ return true;
133
+ const conv = tr.conversation_id ??
134
+ (await t
135
+ .selectFrom("conversations")
136
+ .select("id")
137
+ .where("scheduled_task_id", "=", tr.task_id)
138
+ .where("create_request_id", "=", tr.id)
139
+ .executeTakeFirst())?.id;
140
+ if (conv !== undefined) {
141
+ const run = await t
142
+ .selectFrom("runs")
143
+ .select(["id", "status"])
144
+ .where("conversation_id", "=", conv)
145
+ .where("scheduled_task_run_id", "=", tr.id)
146
+ .orderBy("created_at")
147
+ .limit(1)
148
+ .executeTakeFirst();
149
+ if (run) {
150
+ await t
151
+ .updateTable("scheduled_task_runs")
152
+ .set({ status: "running", run_id: run.id, conversation_id: conv })
153
+ .where("id", "=", tr.id)
154
+ .execute();
155
+ if (!RUN_TERMINAL.has(run.status))
156
+ return true;
157
+ await this.settle(tr, run.status);
158
+ return false;
159
+ }
160
+ const message = await t
161
+ .selectFrom("conversation_events")
162
+ .select("seq")
163
+ .where("conversation_id", "=", conv)
164
+ .where("type", "=", "user_message")
165
+ .where(sql `payload -> 'schedule' ->> 'taskRunId'`, "=", tr.id)
166
+ .limit(1)
167
+ .executeTakeFirst();
168
+ if (message) {
169
+ const c = await t
170
+ .selectFrom("conversations")
171
+ .select("consumed_seq")
172
+ .where("id", "=", conv)
173
+ .executeTakeFirst();
174
+ // posted, its run still to be created (`ensureRun`)
175
+ if (c === undefined || Number(message.seq) > Number(c.consumed_seq))
176
+ return true;
177
+ // taken in by another run (a person's message posted with it): its content was handled
178
+ // and no run of its own will come, so it ends as completed rather than in flight forever
179
+ await this.settle(tr, "completed");
180
+ return false;
181
+ }
182
+ }
183
+ const firedAt = tr.fired_at instanceof Date ? tr.fired_at : new Date(tr.fired_at);
184
+ if (now.getTime() - firedAt.getTime() < START_GRACE_MS)
185
+ return true;
186
+ await t
187
+ .updateTable("scheduled_task_runs")
188
+ .set({ status: "skipped", skip_reason: "removed", fired_at: null })
189
+ .where("id", "=", tr.id)
190
+ .where("status", "=", "pending")
191
+ .execute();
192
+ return false;
193
+ }
194
+ /**
195
+ * A pending task run whose start was refused (the fire job could not create or queue its
196
+ * message, for a reason that is not a lasting skip): it fails and counts against the task like
197
+ * a failed run (`consecutive_failures`, paused at the org's `pauseAfterFailures`), a trial run
198
+ * excepted. In the caller's transaction, which holds the task row.
199
+ */
200
+ async failStartIn(taskRunId, error) {
201
+ const tr = await this.db
202
+ .selectFrom("scheduled_task_runs")
203
+ .select(["id", "task_id", "org_id", "trial", "status"])
204
+ .where("id", "=", taskRunId)
205
+ .forUpdate()
206
+ .executeTakeFirst();
207
+ if (tr?.status !== "pending")
208
+ return;
209
+ await this.settle(tr, "failed", error);
210
+ }
211
+ /** Writes the task run's end and the task's failure count (non-trial runs). */
212
+ async settle(tr, outcome, error) {
213
+ const t = this.db;
214
+ const failed = outcome === "failed";
215
+ await t
216
+ .updateTable("scheduled_task_runs")
217
+ .set({
218
+ status: failed ? "failed" : "completed",
219
+ ...(failed && { error: error ?? "The run failed" }),
220
+ })
221
+ .where("id", "=", tr.id)
222
+ .execute();
223
+ if (tr.trial)
224
+ return;
225
+ if (!failed) {
226
+ await t
227
+ .updateTable("scheduled_tasks")
228
+ .set({ consecutive_failures: 0 })
229
+ .where("id", "=", tr.task_id)
230
+ .where("consecutive_failures", "!=", 0)
231
+ .execute();
232
+ return;
233
+ }
234
+ const row = await t
235
+ .updateTable("scheduled_tasks")
236
+ .set({ consecutive_failures: sql `consecutive_failures + 1` })
237
+ .where("id", "=", tr.task_id)
238
+ .where("deleted_at", "is", null)
239
+ .returning("consecutive_failures")
240
+ .executeTakeFirst();
241
+ if (!row)
242
+ return;
243
+ const settings = await readScheduleSettings(t, this.config, tr.org_id);
244
+ if (row.consecutive_failures >= settings.pauseAfterFailures) {
245
+ await pauseTaskIn(t, tr.task_id, "failures", null);
246
+ }
247
+ }
248
+ };
249
+ __decorate([
250
+ Transactional()
251
+ ], ScheduledTaskRunTracker.prototype, "onRunFinished", null);
252
+ __decorate([
253
+ Transactional({ propagation: "mandatory" })
254
+ ], ScheduledTaskRunTracker.prototype, "inFlightIn", null);
255
+ __decorate([
256
+ Transactional({ propagation: "mandatory" })
257
+ ], ScheduledTaskRunTracker.prototype, "failStartIn", null);
258
+ ScheduledTaskRunTracker = __decorate([
259
+ Injectable(),
260
+ __param(0, Inject(KernelDb)),
261
+ __param(1, Inject(TransactionHost)),
262
+ __param(2, Inject(KernelConfigService))
263
+ ], ScheduledTaskRunTracker);
264
+ export { ScheduledTaskRunTracker };
265
+ /**
266
+ * A kernel run was created for task run `taskRunId` (its message is the run's trigger, or it
267
+ * inherited the marker): a task run still `pending` takes the run (`running`), in the caller's
268
+ * transaction (`RunQueue.ensureRunIn`, which inserts the run). A task run already linked (a
269
+ * sub-agent or wake run inheriting the marker) is left alone.
270
+ */
271
+ export async function linkStartedRunIn(t, taskRunId, runId, conversationId) {
272
+ await t
273
+ .updateTable("scheduled_task_runs")
274
+ .set({
275
+ status: "running",
276
+ run_id: runId,
277
+ conversation_id: sql `COALESCE(conversation_id, ${conversationId})`,
278
+ })
279
+ .where("id", "=", taskRunId)
280
+ .where("status", "=", "pending")
281
+ .where("run_id", "is", null)
282
+ .execute();
283
+ }
284
+ /**
285
+ * A queued run row was taken back before it ran (`RunQueue.deleteQueued`: its job could not be
286
+ * enqueued): the task run it had taken waits for the next run of its message again.
287
+ */
288
+ export async function unlinkRunIn(t, taskRunId, runId) {
289
+ await t
290
+ .updateTable("scheduled_task_runs")
291
+ .set({ status: "pending", run_id: null })
292
+ .where("id", "=", taskRunId)
293
+ .where("run_id", "=", runId)
294
+ .where("status", "=", "running")
295
+ .execute();
296
+ }
297
+ /**
298
+ * A person removed, edited or sent the queued item that carried `schedule` (the item's stored
299
+ * marker, or null): its task run will never run as the task's, so it becomes `skipped`
300
+ * (`removed`) in the caller's transaction, and the next tick is not held by the overlap check.
301
+ */
302
+ export async function releaseQueuedTaskRunIn(t, schedule) {
303
+ const taskRunId = schedule?.taskRunId;
304
+ if (typeof taskRunId !== "string")
305
+ return;
306
+ await t
307
+ .updateTable("scheduled_task_runs")
308
+ .set({ status: "skipped", skip_reason: "removed", fired_at: null })
309
+ .where("id", "=", taskRunId)
310
+ .where("status", "=", "pending")
311
+ .where("run_id", "is", null)
312
+ .execute();
313
+ }
314
+ /**
315
+ * Locks a task's row (deleted or not) in the caller's transaction. Every path that locks a task
316
+ * run and the task takes the task row first: the fire job's claim, the run tracker, the scan's
317
+ * repair (a run finishing while the next tick's claim checks overlap must not deadlock).
318
+ */
319
+ export async function lockTaskRow(t, taskId) {
320
+ await t
321
+ .selectFrom("scheduled_tasks")
322
+ .select("id")
323
+ .where("id", "=", taskId)
324
+ .forUpdate()
325
+ .executeTakeFirst();
326
+ }
327
+ /** The run tracker alone: the runtime (`RunFinish`) imports it without the schedules service. */
328
+ let ScheduledTaskTrackerModule = class ScheduledTaskTrackerModule {
329
+ };
330
+ ScheduledTaskTrackerModule = __decorate([
331
+ Module({ providers: [ScheduledTaskRunTracker], exports: [ScheduledTaskRunTracker] })
332
+ ], ScheduledTaskTrackerModule);
333
+ export { ScheduledTaskTrackerModule };
@@ -0,0 +1,64 @@
1
+ import type { Queue } from "bullmq";
2
+ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
3
+ import { KernelDb } from "../infra/database/kernel-db.js";
4
+ import { TransactionHost } from "../infra/database/transaction-host.js";
5
+ import { LockService } from "../infra/lock/lock.service.js";
6
+ import type { KernelLogger } from "../log.js";
7
+ import { ScheduledTaskRunTracker } from "./run-tracker.js";
8
+ import { ScheduledTaskStore } from "./store.js";
9
+ /**
10
+ * The scheduled task scanner (spec §6), once a minute across replicas. Each due task in its own
11
+ * transaction, locked with `FOR UPDATE SKIP LOCKED` (replicas share the work and never take the
12
+ * same task): the ticks missed outside the org's `catchUpWindowMinutes` become one `skipped` run
13
+ * (`missed`, at the first missed tick), the latest tick in the window gets its run (the unique
14
+ * `(task_id, scheduled_for)` key makes a second insert a no-op), `next_run_at` moves to the next
15
+ * tick plus the task's jitter (or a `once` task ends, or one past its `expires_at`), and after the
16
+ * commit `sched-fire-<taskRunId>` is enqueued. A task whose stored trigger the schedule math cannot
17
+ * evaluate is paused (`invalid_schedule`) instead of failing the scan; a database error fails the
18
+ * pass (the next minute retries) and pauses nothing.
19
+ *
20
+ * Then the repair: a run still `pending` and unfired 10 minutes after its tick has its fire job
21
+ * enqueued again, and is given up (`skipped`, `missed`) after an hour; a run fired an hour ago and
22
+ * still `pending` (its start failed for good) goes through the run tracker's check, which links,
23
+ * settles or releases it, so a `once` task's last run never stays pending.
24
+ */
25
+ export declare class ScheduledTaskScanJob {
26
+ private readonly kdb;
27
+ readonly txHost: TransactionHost;
28
+ private readonly config;
29
+ private readonly store;
30
+ private readonly tracker;
31
+ private readonly maintenance;
32
+ readonly lockService: LockService;
33
+ readonly log: KernelLogger;
34
+ constructor(kdb: KernelDb, txHost: TransactionHost, config: Pick<KernelConfigService, "get">, store: ScheduledTaskStore, tracker: Pick<ScheduledTaskRunTracker, "inFlightIn">, maintenance: Pick<Queue, "add">, lockService: LockService, log: KernelLogger);
35
+ private get db();
36
+ scan(): Promise<void>;
37
+ /**
38
+ * One scan as of `now` (tests pass their own); returns how many due tasks it handled. The repair
39
+ * runs even when the scan loop failed (its error is rethrown afterwards). `repairBatch`: how many
40
+ * rows each repair step takes (tests pass a small one).
41
+ */
42
+ scanAt(now: Date, repairBatch?: number): Promise<number>;
43
+ /**
44
+ * One due task in its own transaction (a task whose trigger cannot be evaluated rolls back
45
+ * alone and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take different tasks. False
46
+ * when no task is due.
47
+ */
48
+ private scanOne;
49
+ /** A task the scan cannot advance stops being due: paused with `invalid_schedule`. */
50
+ private quarantine;
51
+ /** One due task (see the class); returns the id of the run to fire, if any. */
52
+ private advance;
53
+ /** Enqueues the fire jobs (after the commit; a failure is repaired by a later scan). */
54
+ private enqueue;
55
+ /** Lost fire jobs (see the class). */
56
+ private repair;
57
+ /** A run never fired an hour after its tick: given up, task row first (the one lock order). */
58
+ private giveUp;
59
+ /**
60
+ * A started run still pending long after: the tracker links, settles or releases it; one still
61
+ * in flight is stamped (`rechecked_at`) and goes to the back of the next rechecks.
62
+ */
63
+ private recheck;
64
+ }