@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
@@ -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,24 +17,24 @@ 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";
34
32
  import { ToolRegistry } from "../tools/registry.js";
33
+ import { assembleCalendar, CALENDAR_LIMITS, CALENDAR_RANGE_DAYS, CALENDAR_TASK_RANGE_DAYS, calendarRunReadLimit, } from "./calendar.js";
35
34
  import { readScheduleSettings } from "./effective-settings.js";
36
35
  import { grantNotAllowed, scheduleDisabled, scheduleForbidden, scheduleNotFound, targetInvalid, } from "./errors.js";
37
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";
38
- import { schedFireJob } from "./job-ids.js";
37
+ import { oweSchedFire, oweSchedSettleUnfired } from "./job-ids.js";
39
38
  import { pauseTaskIn } from "./pause.js";
40
39
  import { jitterForGap, minGapMinutes, nextTick, validateTrigger } from "./schedule-math.js";
41
40
  import { ScheduledTaskStore } from "./store.js";
@@ -74,14 +73,14 @@ let ScheduledTasksService = class ScheduledTasksService {
74
73
  extensions;
75
74
  providers;
76
75
  log;
77
- maintenance;
76
+ outbox;
78
77
  sandbox;
79
78
  devicePolicy;
80
79
  constructor(db, txHost, config, store, orgLock, agents, access, tools, extensions,
81
80
  /** A task's model must resolve for the org and declare its effort (as a message's switch). */
82
81
  providers, log,
83
- /** "Run now" enqueues the fire job (consumed by the worker role). */
84
- maintenance,
82
+ /** "Run now" owes the fire job (consumed by the worker role). */
83
+ outbox,
85
84
  /** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
86
85
  sandbox,
87
86
  /** Network grants: the task's device's local policy summary (`remoteGrants`). */
@@ -97,7 +96,7 @@ let ScheduledTasksService = class ScheduledTasksService {
97
96
  this.extensions = extensions;
98
97
  this.providers = providers;
99
98
  this.log = log;
100
- this.maintenance = maintenance;
99
+ this.outbox = outbox;
101
100
  this.sandbox = sandbox;
102
101
  this.devicePolicy = devicePolicy;
103
102
  }
@@ -363,6 +362,58 @@ let ScheduledTasksService = class ScheduledTasksService {
363
362
  }
364
363
  return this.withLastRuns(tasks);
365
364
  }
365
+ /**
366
+ * The calendar between two instants (calendar spec §5): the runs and planned occurrences of the
367
+ * actor's own tasks, or with `all` (org admins only, else `forbidden`) every task of the org; with
368
+ * `taskId` only that task (one the actor may see, else `schedule.not_found`), never collapsed.
369
+ * Another creator's runs come without `conversationId`, `error` and `missingGrants` (§15 Q3).
370
+ * A range longer than 43 days (2 with `taskId`), or not forward, is `validation_failed`. `now`
371
+ * is for tests.
372
+ */
373
+ async calendar(orgId, actor, q, now = new Date(), limits = CALENDAR_LIMITS) {
374
+ const role = await this.liveRole(orgId, actor.accountId);
375
+ const manage = can(role, "schedules.manage");
376
+ if (q.all === true && !manage) {
377
+ throw new ApiError("forbidden", "Only org admins can read every scheduled task");
378
+ }
379
+ const span = q.to.getTime() - q.from.getTime();
380
+ const maxDays = q.taskId === undefined ? CALENDAR_RANGE_DAYS : CALENDAR_TASK_RANGE_DAYS;
381
+ if (!(span > 0))
382
+ throw new ApiError("validation_failed", "from must be before to");
383
+ if (span > maxDays * DAY_MS) {
384
+ throw new ApiError("validation_failed", `The range is at most ${maxDays} days${q.taskId === undefined ? "" : " with taskId"}`);
385
+ }
386
+ const t = this.db.db;
387
+ let tasks;
388
+ if (q.taskId !== undefined) {
389
+ const task = await this.store.getTask(t, orgId, q.taskId);
390
+ if (!task || (task.accountId !== actor.accountId && !manage))
391
+ throw scheduleNotFound();
392
+ tasks = [task];
393
+ }
394
+ else {
395
+ tasks =
396
+ q.all === true
397
+ ? await this.store.listForOrg(t, orgId)
398
+ : await this.store.listForAccount(t, orgId, actor.accountId);
399
+ }
400
+ const runs = await this.store.listRunsBetween(t, orgId, tasks.map((x) => x.id), q.from, q.to,
401
+ // one row past the caps tells the assembly where to cut
402
+ calendarRunReadLimit(limits));
403
+ const settings = await readScheduleSettings(t, this.config, orgId);
404
+ return assembleCalendar({
405
+ tasks,
406
+ runs,
407
+ viewerAccountId: actor.accountId,
408
+ from: q.from,
409
+ to: q.to,
410
+ timeZone: q.timeZone,
411
+ now,
412
+ plan: settings.enabled,
413
+ catchUpWindowMinutes: settings.catchUpWindowMinutes,
414
+ collapse: q.taskId === undefined,
415
+ }, limits);
416
+ }
366
417
  /**
367
418
  * The tasks of `tasks` that post into the conversation: a `conversation` task naming it, or a
368
419
  * `chat` task whose chat the conversation is a thread of (spec §10 side panel).
@@ -472,6 +523,8 @@ let ScheduledTasksService = class ScheduledTasksService {
472
523
  disabledTools: d.disabledTools ?? [],
473
524
  subagentTools: d.subagentTools,
474
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",
475
528
  });
476
529
  const ctx = {
477
530
  role,
@@ -736,6 +789,8 @@ let ScheduledTasksService = class ScheduledTasksService {
736
789
  disabledTools: d.disabledTools ?? [],
737
790
  subagentTools: d.subagentTools,
738
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",
739
794
  });
740
795
  return new Map(tools.map((x) => [
741
796
  x.key,
@@ -767,7 +822,7 @@ let ScheduledTasksService = class ScheduledTasksService {
767
822
  /**
768
823
  * "Run now" (spec §6), creator only: a task run for the request time, outside the schedule (no
769
824
  * unique-key conflict with a tick, and `next_run_at` does not move), fired at once through the
770
- * 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
771
826
  * grants (§7). Counted in the rate limit like any run. An ended task cannot run; a paused one
772
827
  * can (only its creator gets here: the run is `manual`, which the fire's paused check lets by).
773
828
  */
@@ -797,10 +852,9 @@ let ScheduledTasksService = class ScheduledTasksService {
797
852
  target: task.id,
798
853
  detail: { taskRunId: run.id, trial: opts.trial },
799
854
  });
800
- const job = schedFireJob(run.id);
801
- afterCommit(async () => {
802
- await this.maintenance.add(job.name, job.data, job.opts);
803
- });
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);
804
858
  return run;
805
859
  }
806
860
  /** The live role of a member of the org; not a member → `schedule.not_found`. */
@@ -1030,6 +1084,8 @@ let ScheduledTasksService = class ScheduledTasksService {
1030
1084
  disabledTools: d.disabledTools ?? [],
1031
1085
  subagentTools: d.subagentTools,
1032
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",
1033
1089
  });
1034
1090
  for (const g of toolGrants) {
1035
1091
  const tool = tools.find((x) => x.key === g.toolKey);
@@ -1115,7 +1171,7 @@ ScheduledTasksService = __decorate([
1115
1171
  __param(8, Inject(ExtensionRegistry)),
1116
1172
  __param(9, Inject(ProviderRegistry)),
1117
1173
  __param(10, Inject(KERNEL_LOGGER)),
1118
- __param(11, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME))),
1174
+ __param(11, Inject(OutboxService)),
1119
1175
  __param(12, Inject(SandboxService)),
1120
1176
  __param(13, Inject(DevicePolicyService))
1121
1177
  ], ScheduledTasksService);
@@ -1,8 +1,9 @@
1
1
  import type { z } from "zod";
2
2
  import { type Principal } from "../identity/principal.js";
3
3
  import { type OrgContext } from "../orgs/org.guard.js";
4
+ import type { CalendarAnswer } from "./calendar.js";
4
5
  import { type ChatTarget, type GrantableTool, type ScheduledTaskGrantView, type ScheduledTaskListItem, ScheduledTasksService } from "./scheduled-tasks.service.js";
5
- import { type ScheduleChatTargetsQuery, type ScheduleCreateBody, type ScheduleGrantableQuery, type ScheduleGrantParams, type ScheduleGrantsBody, type ScheduleListQuery, type ScheduleParams, type ScheduleRunBody, type ScheduleRunsQuery, type ScheduleUpdateBody } from "./schedules.schemas.js";
6
+ import { type ScheduleCalendarQuery, type ScheduleChatTargetsQuery, type ScheduleCreateBody, type ScheduleGrantableQuery, type ScheduleGrantParams, type ScheduleGrantsBody, type ScheduleListQuery, type ScheduleParams, type ScheduleRunBody, type ScheduleRunsQuery, type ScheduleUpdateBody } from "./schedules.schemas.js";
6
7
  import type { ScheduledTask, ScheduledTaskGrant, TaskRun } from "./types.js";
7
8
  /**
8
9
  * Scheduled tasks (`/api/v1/orgs/:orgId/schedules`, spec 2026-11-11-scheduled-tasks §10); human
@@ -10,6 +11,8 @@ import type { ScheduledTask, ScheduledTaskGrant, TaskRun } from "./types.js";
10
11
  * - `GET /` → `{ items }`: the caller's own tasks, each with its `lastRun`; `?all=true` every task
11
12
  * of the org (org admins, `schedules.manage`, enforced by the service: 403 `forbidden`
12
13
  * otherwise); `?conversationId=` only those posting into that conversation;
14
+ * - `GET /calendar?from&to&timeZone[&all][&taskId]` → the calendar (runs, planned occurrences,
15
+ * collapsed task-days; calendar spec §5.1), `org.read` like the list, `all` as the list's;
13
16
  * - `GET /chat-targets` → `{ items }`: the chats the caller can point a `chat` task at;
14
17
  * - `GET /grantable-tools?agentId=` → `{ items }`: the agent's tools, each grantable or why not;
15
18
  * - `POST /` → 201 `{ task }` (`timeZone` is the browser zone, a fallback only);
@@ -30,6 +33,7 @@ export declare class SchedulesController {
30
33
  list(principal: Principal, org: OrgContext, q: z.infer<typeof ScheduleListQuery>): Promise<{
31
34
  items: ScheduledTaskListItem[];
32
35
  }>;
36
+ calendar(principal: Principal, org: OrgContext, q: z.infer<typeof ScheduleCalendarQuery>): Promise<CalendarAnswer>;
33
37
  chatTargets(principal: Principal, org: OrgContext, _q: z.infer<typeof ScheduleChatTargetsQuery>): Promise<{
34
38
  items: ChatTarget[];
35
39
  }>;
@@ -12,13 +12,15 @@ import { CurrentPrincipal, requireUser } from "../identity/principal.js";
12
12
  import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
13
13
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
14
14
  import { ScheduledTasksService, } from "./scheduled-tasks.service.js";
15
- import { createInputOf, grantsOf, ScheduleChatTargetsQueryDto, ScheduleCreateBodyDto, ScheduleGrantableQueryDto, ScheduleGrantParamsDto, ScheduleGrantsBodyDto, ScheduleListQueryDto, ScheduleParamsDto, ScheduleRunBodyDto, ScheduleRunsQueryDto, ScheduleUpdateBodyDto, updateInputOf, } from "./schedules.schemas.js";
15
+ import { calendarInputOf, createInputOf, grantsOf, ScheduleCalendarQueryDto, ScheduleChatTargetsQueryDto, ScheduleCreateBodyDto, ScheduleGrantableQueryDto, ScheduleGrantParamsDto, ScheduleGrantsBodyDto, ScheduleListQueryDto, ScheduleParamsDto, ScheduleRunBodyDto, ScheduleRunsQueryDto, ScheduleUpdateBodyDto, updateInputOf, } from "./schedules.schemas.js";
16
16
  /**
17
17
  * Scheduled tasks (`/api/v1/orgs/:orgId/schedules`, spec 2026-11-11-scheduled-tasks §10); human
18
18
  * users only (a service token gets 403).
19
19
  * - `GET /` → `{ items }`: the caller's own tasks, each with its `lastRun`; `?all=true` every task
20
20
  * of the org (org admins, `schedules.manage`, enforced by the service: 403 `forbidden`
21
21
  * otherwise); `?conversationId=` only those posting into that conversation;
22
+ * - `GET /calendar?from&to&timeZone[&all][&taskId]` → the calendar (runs, planned occurrences,
23
+ * collapsed task-days; calendar spec §5.1), `org.read` like the list, `all` as the list's;
22
24
  * - `GET /chat-targets` → `{ items }`: the chats the caller can point a `chat` task at;
23
25
  * - `GET /grantable-tools?agentId=` → `{ items }`: the agent's tools, each grantable or why not;
24
26
  * - `POST /` → 201 `{ task }` (`timeZone` is the browser zone, a fallback only);
@@ -48,6 +50,11 @@ let SchedulesController = class SchedulesController {
48
50
  });
49
51
  return { items };
50
52
  }
53
+ // declared before `:id`; org.read like the list (a creator demoted to guest still reads their own)
54
+ async calendar(principal, org, q) {
55
+ const p = requireUser(principal);
56
+ return this.tasks.calendar(org.orgId, { accountId: p.account.id }, calendarInputOf(q));
57
+ }
51
58
  async chatTargets(principal, org, _q) {
52
59
  const p = requireUser(principal);
53
60
  return { items: await this.tasks.chatTargets(org.orgId, p.account.id) };
@@ -134,6 +141,13 @@ __decorate([
134
141
  __param(1, CurrentOrg()),
135
142
  __param(2, ZQuery(ScheduleListQueryDto))
136
143
  ], SchedulesController.prototype, "list", null);
144
+ __decorate([
145
+ Get("calendar"),
146
+ OrgAction("org.read"),
147
+ __param(0, CurrentPrincipal()),
148
+ __param(1, CurrentOrg()),
149
+ __param(2, ZQuery(ScheduleCalendarQueryDto))
150
+ ], SchedulesController.prototype, "calendar", null);
137
151
  __decorate([
138
152
  Get("chat-targets"),
139
153
  OrgAction("schedules.use"),
@@ -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],
@@ -138,6 +138,21 @@ export declare const ScheduleListQuery: z.ZodObject<{
138
138
  }>>;
139
139
  conversationId: z.ZodOptional<z.ZodString>;
140
140
  }, z.core.$strict>;
141
+ /**
142
+ * The calendar's range (`from < to`, at most 43 days, 2 with `taskId`: checked by the service),
143
+ * the viewer's zone (local days for the collapse and the cut), `all` (org admins) and `taskId`
144
+ * (one task, never collapsed).
145
+ */
146
+ export declare const ScheduleCalendarQuery: z.ZodObject<{
147
+ from: z.ZodString;
148
+ to: z.ZodString;
149
+ timeZone: z.ZodString;
150
+ all: z.ZodOptional<z.ZodEnum<{
151
+ false: "false";
152
+ true: "true";
153
+ }>>;
154
+ taskId: z.ZodOptional<z.ZodString>;
155
+ }, z.core.$strict>;
141
156
  export declare const ScheduleChatTargetsQuery: z.ZodObject<{}, z.core.$strict>;
142
157
  export declare const ScheduleGrantableQuery: z.ZodObject<{
143
158
  agentId: z.ZodString;
@@ -258,6 +273,18 @@ declare const ScheduleListQueryDto_base: import("../infra/validation/create-zod-
258
273
  }, z.core.$strict>>;
259
274
  export declare class ScheduleListQueryDto extends ScheduleListQueryDto_base {
260
275
  }
276
+ declare const ScheduleCalendarQueryDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
277
+ from: z.ZodString;
278
+ to: z.ZodString;
279
+ timeZone: z.ZodString;
280
+ all: z.ZodOptional<z.ZodEnum<{
281
+ false: "false";
282
+ true: "true";
283
+ }>>;
284
+ taskId: z.ZodOptional<z.ZodString>;
285
+ }, z.core.$strict>>;
286
+ export declare class ScheduleCalendarQueryDto extends ScheduleCalendarQueryDto_base {
287
+ }
261
288
  declare const ScheduleChatTargetsQueryDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{}, z.core.$strict>>;
262
289
  export declare class ScheduleChatTargetsQueryDto extends ScheduleChatTargetsQueryDto_base {
263
290
  }
@@ -313,4 +340,12 @@ export declare function createInputOf(body: z.infer<typeof ScheduleCreateBody>):
313
340
  };
314
341
  /** A patch body as the service's input (an explicit `timeZone` changes the task's zone). */
315
342
  export declare function updateInputOf(body: z.infer<typeof ScheduleUpdateBody>): UpdateTaskInput;
343
+ /** A calendar query as the service's input. */
344
+ export declare const calendarInputOf: (q: z.infer<typeof ScheduleCalendarQuery>) => {
345
+ from: Date;
346
+ to: Date;
347
+ timeZone: string;
348
+ all: boolean;
349
+ taskId?: string;
350
+ };
316
351
  export {};
@@ -86,6 +86,22 @@ export const ScheduleListQuery = z
86
86
  conversationId: Id.optional(),
87
87
  })
88
88
  .strict();
89
+ /** An instant with an offset (`2026-11-09T00:00:00+08:00`). */
90
+ const Instant = z.string().datetime({ offset: true });
91
+ /**
92
+ * The calendar's range (`from < to`, at most 43 days, 2 with `taskId`: checked by the service),
93
+ * the viewer's zone (local days for the collapse and the cut), `all` (org admins) and `taskId`
94
+ * (one task, never collapsed).
95
+ */
96
+ export const ScheduleCalendarQuery = z
97
+ .object({
98
+ from: Instant,
99
+ to: Instant,
100
+ timeZone: IanaTimeZone,
101
+ all: z.enum(["true", "false"]).optional(),
102
+ taskId: Id.optional(),
103
+ })
104
+ .strict();
89
105
  export const ScheduleChatTargetsQuery = z.object({}).strict();
90
106
  export const ScheduleGrantableQuery = z.object({ agentId: Id }).strict();
91
107
  export const ScheduleParams = z.object({ id: Id });
@@ -103,6 +119,8 @@ export class ScheduleUpdateBodyDto extends createZodDto(ScheduleUpdateBody, "Sch
103
119
  }
104
120
  export class ScheduleListQueryDto extends createZodDto(ScheduleListQuery, "ScheduleListQuery") {
105
121
  }
122
+ export class ScheduleCalendarQueryDto extends createZodDto(ScheduleCalendarQuery, "ScheduleCalendarQuery") {
123
+ }
106
124
  export class ScheduleChatTargetsQueryDto extends createZodDto(ScheduleChatTargetsQuery, "ScheduleChatTargetsQuery") {
107
125
  }
108
126
  export class ScheduleGrantableQueryDto extends createZodDto(ScheduleGrantableQuery, "ScheduleGrantableQuery") {
@@ -120,6 +138,7 @@ export class ScheduleGrantParamsDto extends createZodDto(ScheduleGrantParams, "S
120
138
  /** The schedule routes the Platform operations mirror (`check:platform-ops` keys). */
121
139
  export const SCHEDULE_ROUTES = [
122
140
  { route: "GET /api/v1/orgs/:orgId/schedules", query: ScheduleListQuery },
141
+ { route: "GET /api/v1/orgs/:orgId/schedules/calendar", query: ScheduleCalendarQuery },
123
142
  { route: "GET /api/v1/orgs/:orgId/schedules/chat-targets", query: ScheduleChatTargetsQuery },
124
143
  { route: "GET /api/v1/orgs/:orgId/schedules/grantable-tools", query: ScheduleGrantableQuery },
125
144
  { route: "POST /api/v1/orgs/:orgId/schedules", body: ScheduleCreateBody },
@@ -184,3 +203,11 @@ export function updateInputOf(body) {
184
203
  ...(expiresAt !== undefined && { expiresAt: expiresAt === null ? null : new Date(expiresAt) }),
185
204
  };
186
205
  }
206
+ /** A calendar query as the service's input. */
207
+ export const calendarInputOf = (q) => ({
208
+ from: new Date(q.from),
209
+ to: new Date(q.to),
210
+ timeZone: q.timeZone,
211
+ all: q.all === "true",
212
+ ...(q.taskId !== undefined && { taskId: q.taskId }),
213
+ });
@@ -68,6 +68,11 @@ export declare class ScheduledTaskStore {
68
68
  countFiredSince(t: KernelSchemaDb, orgId: string, since: Date): Promise<number>;
69
69
  /** A task's runs, latest tick first. */
70
70
  listRuns(t: KernelSchemaDb, taskId: string, limit: number): Promise<TaskRun[]>;
71
+ /**
72
+ * The runs of these tasks of the org with `scheduled_for` in `[from, to)`, trial included, by tick, at most
73
+ * `limit` (the calendar; `scheduled_task_runs_task_idx` per task).
74
+ */
75
+ listRunsBetween(t: KernelSchemaDb, orgId: string, taskIds: readonly string[], from: Date, to: Date, limit: number): Promise<TaskRun[]>;
71
76
  listGrants(t: KernelSchemaDb, taskId: string): Promise<ScheduledTaskGrant[]>;
72
77
  /** Adds a grant, replacing the task's grant of the same tool key. */
73
78
  addGrant(t: KernelSchemaDb, taskId: string, grant: ScheduledTaskGrant): Promise<void>;
@@ -329,6 +329,26 @@ let ScheduledTaskStore = class ScheduledTaskStore {
329
329
  .execute();
330
330
  return rows.map(toRun);
331
331
  }
332
+ /**
333
+ * The runs of these tasks of the org with `scheduled_for` in `[from, to)`, trial included, by tick, at most
334
+ * `limit` (the calendar; `scheduled_task_runs_task_idx` per task).
335
+ */
336
+ async listRunsBetween(t, orgId, taskIds, from, to, limit) {
337
+ if (taskIds.length === 0)
338
+ return [];
339
+ const rows = await t
340
+ .selectFrom("scheduled_task_runs")
341
+ .selectAll()
342
+ .where("org_id", "=", orgId)
343
+ .where("task_id", "in", [...taskIds])
344
+ .where("scheduled_for", ">=", from)
345
+ .where("scheduled_for", "<", to)
346
+ .orderBy("scheduled_for")
347
+ .orderBy("id")
348
+ .limit(limit)
349
+ .execute();
350
+ return rows.map(toRun);
351
+ }
332
352
  async listGrants(t, taskId) {
333
353
  const rows = await t
334
354
  .selectFrom("scheduled_task_grants")
@@ -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);
@@ -8,6 +8,7 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
8
8
  import { LockService } from "../infra/lock/lock.service.js";
9
9
  import { DevicePolicyService } from "../local-devices/device-policy.service.js";
10
10
  import { type OrgRole } from "../orgs/roles.js";
11
+ import { RunResume } from "../runtime/run-jobs.js";
11
12
  import { SandboxService } from "../sandbox/sandbox.service.js";
12
13
  import { ScheduledTaskStore } from "../schedules/store.js";
13
14
  import type { ApprovalPolicySource } from "./pipeline.js";
@@ -206,10 +207,12 @@ export interface ToolGrantView {
206
207
  * 工具调用审批:新建(loop 调用)、列出 / 查看 / 处理(HTTP),以及「以后都允许」授权的查看与撤销。
207
208
  * 状态全部在 PG:处理决定在事务里 `SELECT … FOR UPDATE` 审批行,多副本并发处理同一审批时恰好一个生效。
208
209
  *
209
- * A decision is announced after the commit as the domain event `approval.decided` (exactly once:
210
- * only the decision that changed the row from pending), which the run's waits follow to resume it
211
- * (spec §3b; the wait sweep resumes a run whose resume was lost). Events are appended in the
212
- * context's transaction (`EventLog.appendIn`) and published after the commit.
210
+ * A decision resumes its waiting run in the decision's transaction (`RunResume.resumeIn`, after the
211
+ * decided row: lock order approval, then run; transactional outbox spec §6). It is announced after
212
+ * the commit as the domain event `approval.decided` (exactly once: only the decision that changed
213
+ * the row from pending; no kernel subscriber: the IM card update is owed by the `approval_decided`
214
+ * append, transactional outbox spec §8.1). Events are appended in the context's transaction
215
+ * (`EventLog.appendIn`) and published after the commit.
213
216
  */
214
217
  export declare class ApprovalsService {
215
218
  private readonly db;
@@ -226,6 +229,8 @@ export declare class ApprovalsService {
226
229
  readonly lockService: LockService;
227
230
  /** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
228
231
  private readonly sandbox;
232
+ /** A decision resumes its waiting run in the decision's transaction. */
233
+ private readonly resumes;
229
234
  constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost, schedules: Pick<ScheduledTaskStore, "addGrant">,
230
235
  /** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
231
236
  registry: Pick<ToolRegistry, "forAgent">,
@@ -234,7 +239,9 @@ export declare class ApprovalsService {
234
239
  /** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
235
240
  lockService: LockService,
236
241
  /** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
237
- sandbox: Pick<SandboxService, "ceilingLimits">);
242
+ sandbox: Pick<SandboxService, "ceilingLimits">,
243
+ /** A decision resumes its waiting run in the decision's transaction. */
244
+ resumes: Pick<RunResume, "resumeIn">);
238
245
  /**
239
246
  * 新建 pending 审批(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `approval_requested`,同一事务。
240
247
  * 按 (run_id, call_id, attempt) 幂等:已存在则返回已有记录(不覆盖),事件按 `approval_requested:<approvalId>` 幂等。
@@ -331,11 +338,22 @@ export declare class ApprovalsService {
331
338
  */
332
339
  cancelPendingIn(runId: string): Promise<ApprovalRecord[]>;
333
340
  /**
334
- * 在调用方的事务内(`mandatory`)取消某组织里指定由 `accountId` 处理(`requested_for`)的全部 pending 审批(成员离职),
335
- * 走与 {@link cancelPendingIn} 相同的取消路径:置 cancelled、写 `approval_decided{cancelled}`(处理人为 null)。
336
- * 返回被取消的记录与各会话写到的最大 seq;调用方提交后发布事件(并可续跑对应运行)。
341
+ * The conversations of `orgId` holding a pending approval `accountId` is asked to decide
342
+ * (`requested_for`), read without locks: offboarding locks them, by tree root first
343
+ * (`lockConversationTrees`), before {@link cancelPendingFor}.
337
344
  */
338
- cancelPendingFor(orgId: string, accountId: string): Promise<{
345
+ pendingConversationsFor(orgId: string, accountId: string): Promise<string[]>;
346
+ /**
347
+ * In the caller's transaction (`mandatory`; offboarding): cancels every pending approval of
348
+ * `orgId` that `accountId` is asked to decide (`requested_for`) in `conversationIds`, the
349
+ * conversations the caller already locked (by tree root first, `lockConversationTrees`; lock
350
+ * order: conversations, then approval rows), along the same path as {@link cancelPendingIn}:
351
+ * `cancelled`, `approval_decided{cancelled}` with no decider. An approval asked meanwhile in
352
+ * another conversation is left to its run's stop, so no conversation is locked after a row.
353
+ * Returns the cancelled records and the highest seq written per conversation; the caller
354
+ * publishes after the commit.
355
+ */
356
+ cancelPendingFor(orgId: string, accountId: string, conversationIds: readonly string[]): Promise<{
339
357
  records: ApprovalRecord[];
340
358
  seqs: Map<string, number>;
341
359
  }>;
@@ -346,13 +364,25 @@ export declare class ApprovalsService {
346
364
  * active runs `authorAccountId` started, in the conversation and in its sub-agents'
347
365
  * conversations at any depth (their asks are shown in the root). Each becomes `skipped` with
348
366
  * `approval_decided{skipped}` by the author; access stays blocked. Returns the skipped records and
349
- * the highest seq written per conversation; the caller notifies and emits
350
- * `approval.decided{skipped}` after the commit, so the runs resume.
367
+ * the highest seq written per conversation; the caller resumes their waiting runs in this
368
+ * transaction once every skipped row is locked (`ConversationsService.skipWaitsIn`), and notifies
369
+ * and emits the domain event `approval.decided{skipped}` after the commit (its IM card update is
370
+ * owed by the append, transactional outbox spec §8.1).
351
371
  */
352
372
  skipNetworkIn(conversationId: string, authorAccountId: string): Promise<{
353
373
  records: ApprovalRecord[];
354
374
  seqs: Map<string, number>;
355
375
  }>;
376
+ /**
377
+ * Locks the conversation row of approval `id` (`SELECT … FOR UPDATE`; re-entrant with the lock
378
+ * `EventLog.appendIn` takes later). Every write to an approval row takes its conversation's lock
379
+ * first, then the approval row, then the run row, as the stop of a waiting run
380
+ * (`Waits.abortWaitingIn`) and the questions do, so no two of them can deadlock. False: no such
381
+ * approval (in `orgId`).
382
+ */
383
+ private lockConversationOf;
384
+ /** Locks, in id order, the conversations `ids` selects; returns their ids. */
385
+ private lockConversations;
356
386
  /** `conversationId` and its sub-agents' conversations at any depth (at most 16 levels). */
357
387
  private subtreeOf;
358
388
  /**