@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
@@ -4,6 +4,7 @@ import { OwnedPluginCleanup } from "../catalog/owned-plugin-cleanup.js";
4
4
  import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
5
5
  import type { KernelLogger } from "../log.js";
6
6
  import { DirectorySync } from "../orgs/directory-sync.js";
7
+ import { Offboarding } from "../orgs/offboarding.js";
7
8
  import { SandboxService } from "../sandbox/sandbox.service.js";
8
9
  import { ScheduledTaskFirer } from "../schedules/fire.js";
9
10
  import { ToolDrift } from "../tools/drift.js";
@@ -16,8 +17,10 @@ export declare const MAINTENANCE_CONCURRENCY = 5;
16
17
  * The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
17
18
  * each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
18
19
  * `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
19
- * {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `owned-plugin-cleanup` →
20
- * {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
20
+ * {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
21
+ * {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
22
+ * runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
23
+ * {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
21
24
  * such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
22
25
  * maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
23
26
  * test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
@@ -28,13 +31,14 @@ export declare class MaintenanceProcessor extends WorkerHost<Worker> {
28
31
  private readonly sandbox;
29
32
  private readonly drift;
30
33
  private readonly directory;
34
+ private readonly offboarding;
31
35
  private readonly runs;
32
36
  private readonly ownedCleanup;
33
37
  private readonly scheduledTasks;
34
38
  private readonly scheduled;
35
39
  private readonly log;
36
40
  private readonly queue;
37
- constructor(waits: Pick<Waits, "processTimeout">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
41
+ constructor(waits: Pick<Waits, "processTimeout" | "childFinished" | "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, offboarding: Pick<Offboarding, "stopRun">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire" | "settle">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
38
42
  /**
39
43
  * Removes the retired job schedulers (`RETIRED_SCHEDULER_IDS`, runtime/retired-schedulers.ts): every fixed-period
40
44
  * maintenance job is a `@LockedCron` / `@LockedInterval` now. Idempotent; called once when a
@@ -20,19 +20,21 @@ import { KernelScheduleModule } from "../infra/schedule/schedule.module.js";
20
20
  import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
21
21
  import { KERNEL_LOGGER } from "../infra/tokens.js";
22
22
  import { DIRECTORY_SYNC_JOB, DirectorySync } from "../orgs/directory-sync.js";
23
+ import { Offboarding, RUN_STOP_JOB } from "../orgs/offboarding.js";
23
24
  import { OrgsModule } from "../orgs/orgs.module.js";
24
25
  import { runSandboxDestroyFor, runSandboxResetFiles, SANDBOX_DESTROY_FOR_JOB, SANDBOX_RESET_FILES_JOB, } from "../sandbox/jobs.js";
25
26
  import { SandboxModule } from "../sandbox/sandbox.module.js";
26
27
  import { SandboxService } from "../sandbox/sandbox.service.js";
27
28
  import { ScheduledTaskFireModule, ScheduledTaskFirer } from "../schedules/fire.js";
28
- import { SCHED_FIRE_JOB } from "../schedules/job-ids.js";
29
+ import { SCHED_FIRE_JOB, SCHED_SETTLE_JOB, } from "../schedules/job-ids.js";
29
30
  import { ToolDrift } from "../tools/drift.js";
30
31
  import { DRIFT_CONNECTOR_JOB } from "../tools/job-ids.js";
31
32
  import { ToolsModule } from "../tools/tools.module.js";
32
33
  import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
33
34
  import { RETIRED_SCHEDULER_IDS } from "./retired-schedulers.js";
34
- import { RunQueue } from "./run-queue.js";
35
+ import { ENSURE_RUN_JOB, RunQueue } from "./run-queue.js";
35
36
  import { RuntimeModule } from "./runtime.module.js";
37
+ import { DELIVER_TO_PARENT_JOB, processDeliverToParent, processStopTree, STOP_TREE_JOB, } from "./subagents/tree-jobs.js";
36
38
  import { WAIT_TIMEOUT_JOB, Waits } from "./waits.js";
37
39
  export { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
38
40
  /** The maintenance worker's concurrency (as the hand-made worker had). */
@@ -41,8 +43,10 @@ export const MAINTENANCE_CONCURRENCY = 5;
41
43
  * The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
42
44
  * each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
43
45
  * `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
44
- * {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `owned-plugin-cleanup` →
45
- * {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
46
+ * {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
47
+ * {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
48
+ * runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
49
+ * {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
46
50
  * such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
47
51
  * maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
48
52
  * test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
@@ -53,18 +57,20 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
53
57
  sandbox;
54
58
  drift;
55
59
  directory;
60
+ offboarding;
56
61
  runs;
57
62
  ownedCleanup;
58
63
  scheduledTasks;
59
64
  scheduled;
60
65
  log;
61
66
  queue;
62
- constructor(waits, sandbox, drift, directory, runs, ownedCleanup, scheduledTasks, scheduled, log, queue) {
67
+ constructor(waits, sandbox, drift, directory, offboarding, runs, ownedCleanup, scheduledTasks, scheduled, log, queue) {
63
68
  super();
64
69
  this.waits = waits;
65
70
  this.sandbox = sandbox;
66
71
  this.drift = drift;
67
72
  this.directory = directory;
73
+ this.offboarding = offboarding;
68
74
  this.runs = runs;
69
75
  this.ownedCleanup = ownedCleanup;
70
76
  this.scheduledTasks = scheduledTasks;
@@ -106,6 +112,23 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
106
112
  case DIRECTORY_SYNC_JOB:
107
113
  await this.directory.runSyncJob(job.data);
108
114
  return;
115
+ // a departed member's run stop, owed by the offboarding (transactional outbox spec §8.1)
116
+ case RUN_STOP_JOB:
117
+ try {
118
+ await this.offboarding.stopRun(job.data);
119
+ }
120
+ catch (err) {
121
+ // nothing stops the run after its last attempt: say so (the failed job is kept)
122
+ if (job.attemptsMade + 1 >= (job.opts.attempts ?? 1)) {
123
+ this.log.error("a departed member's run could not be stopped", {
124
+ jobId: job.id,
125
+ runId: job.data.runId,
126
+ err,
127
+ });
128
+ }
129
+ throw err;
130
+ }
131
+ return;
109
132
  // the end of a conversation's wake window (background Bash spec §3)
110
133
  case BG_WAKE_JOB: {
111
134
  // fired before the window ended (clock skew): it reschedules under its next retry id
@@ -113,6 +136,18 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
113
136
  await this.runs.ensureRun(conversationId, { wakeRetry: (retry ?? 0) + 1 });
114
137
  return;
115
138
  }
139
+ // the ensure-run backstop of a message, wake or run end (transactional outbox spec §6)
140
+ case ENSURE_RUN_JOB:
141
+ await this.runs.ensureRun(job.data.conversationId);
142
+ return;
143
+ // the stops a run's end or a root's delete owes its sub-agent tree (outbox spec §6)
144
+ case STOP_TREE_JOB:
145
+ await processStopTree(this.waits, job.data);
146
+ return;
147
+ // a finished sub-agent run's delivery to its parent (transactional outbox spec §6)
148
+ case DELIVER_TO_PARENT_JOB:
149
+ await processDeliverToParent(this.waits, job.data);
150
+ return;
116
151
  // a removed org-owned plugin's extension (plugin scope phase 3, P3-20)
117
152
  case OWNED_PLUGIN_CLEANUP_JOB:
118
153
  await this.ownedCleanup.run(job.data.itemId);
@@ -121,6 +156,10 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
121
156
  case SCHED_FIRE_JOB:
122
157
  await this.scheduledTasks.fire(job.data.taskRunId);
123
158
  return;
159
+ // a task run's settle check, claimed or unfired (ruling R26)
160
+ case SCHED_SETTLE_JOB:
161
+ await this.scheduledTasks.settle(job.data);
162
+ return;
124
163
  default:
125
164
  if (await this.scheduled.runNow(job.name))
126
165
  return;
@@ -146,12 +185,13 @@ MaintenanceProcessor = __decorate([
146
185
  __param(1, Inject(SandboxService)),
147
186
  __param(2, Inject(ToolDrift)),
148
187
  __param(3, Inject(DirectorySync)),
149
- __param(4, Inject(RunQueue)),
150
- __param(5, Inject(OwnedPluginCleanup)),
151
- __param(6, Inject(ScheduledTaskFirer)),
152
- __param(7, Inject(ScheduledRuns)),
153
- __param(8, Inject(KERNEL_LOGGER)),
154
- __param(9, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
188
+ __param(4, Inject(Offboarding)),
189
+ __param(5, Inject(RunQueue)),
190
+ __param(6, Inject(OwnedPluginCleanup)),
191
+ __param(7, Inject(ScheduledTaskFirer)),
192
+ __param(8, Inject(ScheduledRuns)),
193
+ __param(9, Inject(KERNEL_LOGGER)),
194
+ __param(10, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
155
195
  ], MaintenanceProcessor);
156
196
  export { MaintenanceProcessor };
157
197
  /**
@@ -171,7 +211,7 @@ MaintenanceModule = __decorate([
171
211
  SandboxModule,
172
212
  ToolsModule,
173
213
  OrgsModule,
174
- // the removed org plugins' cleanup and its daily sweep (worker-only through this module)
214
+ // the removed org plugins' cleanup job (worker-only through this module)
175
215
  OwnedPluginCleanupModule,
176
216
  // the scheduled task fire job and the scanner (worker-only through this module)
177
217
  ScheduledTaskFireModule,
@@ -1,4 +1,4 @@
1
- import { type ContentBlock, type Logger, type ModelRequest, type ModelStreamEvent, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
1
+ import { type ContentBlock, type ConversationSource, type Logger, type ModelRequest, type ModelStreamEvent, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
2
2
  import type { AgentDefinition } from "../agents/definition.js";
3
3
  import type { AppendEvent, EventLog } from "../conversations/event-log.js";
4
4
  import { type ConversationEvent } from "../conversations/events.js";
@@ -11,7 +11,6 @@ import type { SubagentParent } from "./loop.js";
11
11
  import { MediaCache } from "./media-cache.js";
12
12
  import { type BoundProvider, type ProviderRegistry } from "./providers.js";
13
13
  import type { StepLog } from "./step-log.js";
14
- import type { UsageReporter } from "./usage-report.js";
15
14
  /** 单轮收尾的尾部提醒(模型可见,英文;spec 6 轮次额度收尾)。 */
16
15
  export declare const WRAP_UP_REMINDER = "Step limit reached. Do not call tools. Summarize what you found and what remains for the user.";
17
16
  /**
@@ -48,8 +47,11 @@ export type StreamSink = (delta: {
48
47
  export interface ModelStepDeps {
49
48
  db: KernelSchemaDb;
50
49
  eventLog: EventLog;
51
- /** The step's transactional writes (a model call with its tool specs, a denial with its audit row, a sub-agent's counted usage). */
52
- stepLog: Pick<StepLog, "appendCounted" | "appendAudited" | "appendWithSpecs">;
50
+ /**
51
+ * The step's transactional writes (a model call with its tool specs, a denial with its audit row,
52
+ * usage events with their owed `usage.report` deliveries, a sub-agent's counted usage).
53
+ */
54
+ stepLog: Pick<StepLog, "appendCounted" | "appendReported" | "appendAudited" | "appendWithSpecs">;
53
55
  registry: Pick<ProviderRegistry, "resolve">;
54
56
  /** 工具注册表:每步开始时解析 agent 引用的工具(状态可能在步骤之间变化)。 */
55
57
  tools: Pick<ToolRegistry, "forAgent">;
@@ -67,11 +69,6 @@ export interface ModelStepDeps {
67
69
  * `conversationKind: "chat"` on the step input.
68
70
  */
69
71
  hooks?: HookRunner;
70
- /**
71
- * `usage.report` delivery (P-17): each written `usage` event is enqueued (chat runs and full-run eval
72
- * replays, which both log usage). `enqueue` never throws and returns at once without handlers.
73
- */
74
- usageReport?: Pick<UsageReporter, "enqueue">;
75
72
  }
76
73
  /** 构建请求用到的附件来源。 */
77
74
  export interface MediaSourceDeps {
@@ -107,6 +104,11 @@ export interface ModelStepInput {
107
104
  /** Hook payload scope: the run's agent, its trigger account and the conversation kind. */
108
105
  agentId?: string;
109
106
  accountId?: string;
107
+ /**
108
+ * The conversation's source (memory plugin spec §6.2): extension tools limited by
109
+ * `spec.sources` are listed by it.
110
+ */
111
+ source: ConversationSource;
110
112
  /** Hooks fire for `chat` and `subagent` (never for eval replays). */
111
113
  conversationKind?: ConversationKindForHooks;
112
114
  /** A sub-agent's run: where it was dispatched from (hook payloads carry it as `parent`). */
@@ -121,7 +123,8 @@ export interface ModelStepInput {
121
123
  parentRunId?: string;
122
124
  /**
123
125
  * The run's step limit (`LoopContext.limits.maxSteps`) for the turn-limit reminder; a child's
124
- * may be below `runtime.maxSteps` (final review m4). Defaults to `runtime.maxSteps`.
126
+ * may be below `runtime.maxSteps` (final review m4). Absent: the run has no step limit, and no
127
+ * reminder is sent.
125
128
  */
126
129
  maxSteps?: number;
127
130
  }
@@ -176,15 +179,15 @@ export declare function runModelStep(deps: ModelStepDeps, input: ModelStepInput)
176
179
  export declare function runFinalWrapUp(deps: ModelStepDeps, input: ModelStepInput, reason?: WrapUpReason): Promise<ModelStepResult>;
177
180
  /**
178
181
  * Appends a model call's events (its reply or compaction outcome with its `usage`; also the usage
179
- * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`). For a
180
- * sub-agent's run (`input.parentRunId`) in one transaction that also adds the tokens of the usage
181
- * events it newly wrote to the budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage
182
- * event already in the log (an idempotent hit after a crash) adds nothing, so each call counts
183
- * exactly once.
182
+ * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
183
+ * owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
184
+ * §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
185
+ * budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
186
+ * idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
184
187
  */
185
188
  export declare function appendCounted(deps: {
186
- eventLog: Pick<EventLog, "append" | "notify">;
187
- stepLog: Pick<StepLog, "appendCounted">;
189
+ eventLog: Pick<EventLog, "notify">;
190
+ stepLog: Pick<StepLog, "appendCounted" | "appendReported">;
188
191
  }, input: {
189
192
  conversationId: string;
190
193
  parentRunId?: string | undefined;
@@ -123,12 +123,6 @@ wrapUpReason) {
123
123
  const { conversationId, runId, step, signal, sink } = input;
124
124
  const existing = await findExisting(deps.eventLog, conversationId, runId, step);
125
125
  if (existing) {
126
- // resumed after a crash between writing usage and enqueueing it: enqueue again (job id dedupes)
127
- if (deps.usageReport && existing.usage !== null) {
128
- const u = await deps.eventLog.findByIdem(conversationId, idem.usage(runId, step));
129
- if (u)
130
- void deps.usageReport.enqueue(conversationId, u.seq);
131
- }
132
126
  // a crash between the assistant_message and clearing its draft leaves one behind
133
127
  sink({ kind: "step_end", runId, step });
134
128
  return existing;
@@ -165,6 +159,7 @@ wrapUpReason) {
165
159
  forkChild: input.parent?.forSkill === true,
166
160
  sandboxConversationId: input.parent?.rootConversationId ?? input.conversationId,
167
161
  replay: input.conversationKind === "eval",
162
+ source: input.source,
168
163
  skillLoad: skillLoadOffered(events.findLast((e) => e.type === "skills_catalog"), hasInvocation(events)),
169
164
  });
170
165
  const tools = {
@@ -230,8 +225,9 @@ wrapUpReason) {
230
225
  }
231
226
  }
232
227
  const { outcome, ref } = await ladder(deps, input, events, runtime, tools, wrapUpReason, streamed);
233
- // assistant_message 与 usage 同一次 append = 同一事务。
234
- const appended = await appendCounted(deps, input, [
228
+ // assistant_message and usage in one append = one transaction, which also owes the usage's
229
+ // usage.report delivery (transactional outbox spec §8.1)
230
+ await appendCounted(deps, input, [
235
231
  {
236
232
  type: "assistant_message",
237
233
  runId,
@@ -245,10 +241,6 @@ wrapUpReason) {
245
241
  payload: { step, provider: ref.provider, model: ref.model, ...outcome.usage },
246
242
  },
247
243
  ]);
248
- // usage.report metering (never awaited on the run's path; enqueue logs and swallows failures)
249
- const usageEvent = appended.find((e) => e.type === "usage");
250
- if (deps.usageReport && usageEvent)
251
- void deps.usageReport.enqueue(conversationId, usageEvent.seq);
252
244
  sink({ kind: "step_end", runId, step });
253
245
  return {
254
246
  content: outcome.content,
@@ -277,18 +269,18 @@ wrapUpReason) {
277
269
  }
278
270
  /**
279
271
  * Appends a model call's events (its reply or compaction outcome with its `usage`; also the usage
280
- * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`). For a
281
- * sub-agent's run (`input.parentRunId`) in one transaction that also adds the tokens of the usage
282
- * events it newly wrote to the budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage
283
- * event already in the log (an idempotent hit after a crash) adds nothing, so each call counts
284
- * exactly once.
272
+ * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
273
+ * owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
274
+ * §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
275
+ * budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
276
+ * idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
285
277
  */
286
278
  export async function appendCounted(deps, input, events) {
287
279
  const { conversationId, parentRunId } = input;
288
- if (parentRunId === undefined)
289
- return deps.eventLog.append(conversationId, events);
290
280
  const budgetRunId = input.budgetRunId ?? input.parent?.budgetRunId;
291
- const out = await deps.stepLog.appendCounted(conversationId, { parentRunId, ...(budgetRunId !== undefined && { budgetRunId }) }, events);
281
+ const out = parentRunId === undefined
282
+ ? await deps.stepLog.appendReported(conversationId, events)
283
+ : await deps.stepLog.appendCounted(conversationId, { parentRunId, ...(budgetRunId !== undefined && { budgetRunId }) }, events);
292
284
  if (out.toSeq !== null)
293
285
  await deps.eventLog.notify(conversationId, out.toSeq);
294
286
  return out.written;
@@ -691,9 +683,6 @@ async function maybeCompact(deps, input, events, runtime, tools) {
691
683
  });
692
684
  }
693
685
  const written = await appendCounted(deps, input, batch);
694
- const usageEvent = written.find((e) => e.type === "usage");
695
- if (deps.usageReport && usageEvent)
696
- void deps.usageReport.enqueue(conversationId, usageEvent.seq);
697
686
  return mergeLocal(events, written);
698
687
  }
699
688
  /** Merges written events into the local list (idempotent hits already in it are not added twice). */
@@ -719,7 +708,9 @@ async function ladder(deps, input, events, runtime, tools, wrapUpReason, streame
719
708
  ? BUDGET_WRAP_UP_REMINDER
720
709
  : wrapUp
721
710
  ? WRAP_UP_REMINDER
722
- : turnLimitReminder(step, input.maxSteps ?? runtime.maxSteps);
711
+ : input.maxSteps === undefined
712
+ ? null
713
+ : turnLimitReminder(step, input.maxSteps);
723
714
  let evs = events;
724
715
  const rungs = [
725
716
  { ref: agent.model, fallback: false },
@@ -0,0 +1,69 @@
1
+ import { type ConversationSource } from "@capybara-hq/plugin-sdk";
2
+ import { EventLog } from "../conversations/event-log.js";
3
+ import { ExtensionPointDefaults } from "../extensions/point-defaults.js";
4
+ import { ExtensionRegistry } from "../extensions/registry.js";
5
+ import { KernelDb } from "../infra/database/kernel-db.js";
6
+ import { TransactionHost } from "../infra/database/transaction-host.js";
7
+ import type { KernelLogger } from "../log.js";
8
+ import type { OrgRole } from "../orgs/roles.js";
9
+ /** What a run start tells {@link PluginContextService.record}. */
10
+ export interface PluginContextInput {
11
+ orgId: string;
12
+ conversationId: string;
13
+ runId: string;
14
+ agentId: string;
15
+ /** The run's agent definition name. */
16
+ agentName: string;
17
+ /** The conversation's owner (`conversations.account_id`) and display name. */
18
+ ownerAccountId: string;
19
+ ownerName: string;
20
+ /** The account whose message started the run. */
21
+ triggerAccountId: string;
22
+ source: ConversationSource;
23
+ /** The trigger account's role in the org. */
24
+ role: OrgRole;
25
+ /** The conversation's kind: only a chat run asks the providers. */
26
+ kind: "chat" | "eval" | "subagent";
27
+ /**
28
+ * The keys of the run's resolved tool set, by plugin (resolved with `forAgent` once, and only
29
+ * when some provider is asked).
30
+ */
31
+ toolKeysByPlugin: () => Promise<Map<string, string[]>>;
32
+ /** Aborts every provider call (with its own time limit). */
33
+ signal: AbortSignal;
34
+ }
35
+ /**
36
+ * Run start's plugin context (memory plugin spec §6.1): asks every `context@1` provider enabled in
37
+ * the org (valid config) for its text, in parallel and outside any transaction, each under the
38
+ * run's signal and {@link CONTEXT_LIMITS}.timeoutMs; then appends a `plugin_context` event for each
39
+ * plugin whose text differs from the latest one it recorded in the conversation. Fail open: a
40
+ * provider that throws, times out or returns too much is skipped for this run (its earlier text
41
+ * stays in effect) and logged. A provider loaded here that recorded text but no longer serves the
42
+ * org (switched off, its stored org config or its effective config invalid) gets an empty event,
43
+ * so its text stops following summaries; a plugin not loaded on this replica is left alone
44
+ * (another replica may still load it during a rollout, and blanking it here would make its text
45
+ * flap), and so is one whose status could not be read (a transient read error must not flip the
46
+ * model's context). Chat runs only: an
47
+ * eval replay reads the recorded events, and a sub-agent gets no plugin context (spec §5.4).
48
+ */
49
+ export declare class PluginContextService {
50
+ private readonly db;
51
+ private readonly eventLog;
52
+ readonly txHost: TransactionHost;
53
+ private readonly points;
54
+ private readonly registry;
55
+ private readonly log;
56
+ constructor(db: KernelDb, eventLog: Pick<EventLog, "appendIn" | "notify">, txHost: TransactionHost, points: Pick<ExtensionPointDefaults, "providersFor">, registry: Pick<ExtensionRegistry, "list">, log: KernelLogger);
57
+ /** Asks the providers and records what changed (see the class); notifies after the commit. */
58
+ record(input: PluginContextInput): Promise<void>;
59
+ /** One provider's call, bounded by the run's signal and the time limit. */
60
+ private ask;
61
+ /**
62
+ * One transaction under the conversation row lock (as `RunContextService`): compares each
63
+ * answer's hash with the plugin's latest `plugin_context` and appends the ones that differ (a
64
+ * plugin with no earlier event and no text writes nothing), plus an empty event for each plugin
65
+ * in `off` whose latest text is not empty. Idem key per run and plugin, so a retried run
66
+ * start finds its events or nothing changed. Returns the last seq written.
67
+ */
68
+ private append;
69
+ }
@@ -0,0 +1,208 @@
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 { createHash } from "node:crypto";
11
+ import { CONTEXT_LIMITS, } from "@capybara-hq/plugin-sdk";
12
+ import { Inject, Injectable } from "@nestjs/common";
13
+ import { EventLog } from "../conversations/event-log.js";
14
+ import { ExtensionPointDefaults } from "../extensions/point-defaults.js";
15
+ import { ExtensionRegistry } from "../extensions/registry.js";
16
+ import { KernelDb } from "../infra/database/kernel-db.js";
17
+ import { TransactionHost } from "../infra/database/transaction-host.js";
18
+ import { Transactional } from "../infra/database/transactional.decorator.js";
19
+ import { KERNEL_LOGGER } from "../infra/tokens.js";
20
+ import { latestPluginContexts, pluginContextIdemKey } from "./context/plugin-context.js";
21
+ const sha256 = (text) => createHash("sha256").update(text).digest("hex");
22
+ /**
23
+ * Run start's plugin context (memory plugin spec §6.1): asks every `context@1` provider enabled in
24
+ * the org (valid config) for its text, in parallel and outside any transaction, each under the
25
+ * run's signal and {@link CONTEXT_LIMITS}.timeoutMs; then appends a `plugin_context` event for each
26
+ * plugin whose text differs from the latest one it recorded in the conversation. Fail open: a
27
+ * provider that throws, times out or returns too much is skipped for this run (its earlier text
28
+ * stays in effect) and logged. A provider loaded here that recorded text but no longer serves the
29
+ * org (switched off, its stored org config or its effective config invalid) gets an empty event,
30
+ * so its text stops following summaries; a plugin not loaded on this replica is left alone
31
+ * (another replica may still load it during a rollout, and blanking it here would make its text
32
+ * flap), and so is one whose status could not be read (a transient read error must not flip the
33
+ * model's context). Chat runs only: an
34
+ * eval replay reads the recorded events, and a sub-agent gets no plugin context (spec §5.4).
35
+ */
36
+ let PluginContextService = class PluginContextService {
37
+ db;
38
+ eventLog;
39
+ txHost;
40
+ points;
41
+ registry;
42
+ log;
43
+ constructor(db, eventLog, txHost, points, registry, log) {
44
+ this.db = db;
45
+ this.eventLog = eventLog;
46
+ this.txHost = txHost;
47
+ this.points = points;
48
+ this.registry = registry;
49
+ this.log = log;
50
+ }
51
+ /** Asks the providers and records what changed (see the class); notifies after the commit. */
52
+ async record(input) {
53
+ if (input.kind !== "chat")
54
+ return;
55
+ let found;
56
+ try {
57
+ found = await this.points.providersFor(input.orgId, "context");
58
+ }
59
+ catch (err) {
60
+ // fail open: without the org's providers nothing is asked and nothing is switched off
61
+ this.log.warn("context providers could not be listed; plugin context left as recorded", {
62
+ orgId: input.orgId,
63
+ conversationId: input.conversationId,
64
+ runId: input.runId,
65
+ err,
66
+ });
67
+ return;
68
+ }
69
+ const { providers } = found;
70
+ const tools = providers.length > 0 ? await input.toolKeysByPlugin() : new Map();
71
+ const answers = await Promise.all(providers.map(async (p) => {
72
+ const answer = await this.ask(p, input, tools.get(p.extension) ?? []);
73
+ if (!answer.ok) {
74
+ this.log.warn("context provider skipped for this run", {
75
+ orgId: input.orgId,
76
+ conversationId: input.conversationId,
77
+ runId: input.runId,
78
+ plugin: p.extension,
79
+ reason: answer.reason,
80
+ });
81
+ }
82
+ return [p.extension, answer];
83
+ }));
84
+ const texts = new Map();
85
+ for (const [plugin, answer] of answers)
86
+ if (answer.ok)
87
+ texts.set(plugin, answer.text);
88
+ const serving = new Set(providers.map((p) => p.extension));
89
+ const off = new Set(this.registry
90
+ .list("context")
91
+ .map((e) => e.plugin)
92
+ // a provider whose status could not be read (a transient error) keeps its recorded text
93
+ .filter((plugin) => !serving.has(plugin) && !found.failed.has(plugin)));
94
+ // nothing answered and nothing to switch off: no transaction, no row lock
95
+ if (texts.size === 0 && off.size === 0)
96
+ return;
97
+ const seq = await this.append(input, texts, off);
98
+ if (seq !== null)
99
+ await this.eventLog.notify(input.conversationId, seq);
100
+ }
101
+ /** One provider's call, bounded by the run's signal and the time limit. */
102
+ async ask(p, input, tools) {
103
+ const signal = AbortSignal.any([input.signal, AbortSignal.timeout(CONTEXT_LIMITS.timeoutMs)]);
104
+ let result;
105
+ try {
106
+ result = await untilAborted(Promise.resolve().then(() => p.impl.provide({
107
+ orgId: input.orgId,
108
+ accountId: input.triggerAccountId,
109
+ agentId: input.agentId,
110
+ agentName: input.agentName,
111
+ conversationId: input.conversationId,
112
+ runId: input.runId,
113
+ source: input.source,
114
+ role: input.role,
115
+ ownerAccountId: input.ownerAccountId,
116
+ ownerName: input.ownerName,
117
+ tools,
118
+ config: p.config,
119
+ signal,
120
+ })), signal);
121
+ }
122
+ catch (err) {
123
+ if (signal.aborted) {
124
+ return { ok: false, reason: input.signal.aborted ? "run aborted" : "timed out" };
125
+ }
126
+ return { ok: false, reason: `threw: ${err instanceof Error ? err.message : String(err)}` };
127
+ }
128
+ if (result === null)
129
+ return { ok: true, text: "" };
130
+ const text = result?.text;
131
+ if (typeof text !== "string")
132
+ return { ok: false, reason: "returned no text" };
133
+ const bytes = Buffer.byteLength(text, "utf8");
134
+ if (bytes > CONTEXT_LIMITS.maxBytes) {
135
+ return { ok: false, reason: `text of ${bytes} bytes is over ${CONTEXT_LIMITS.maxBytes}` };
136
+ }
137
+ return { ok: true, text };
138
+ }
139
+ /**
140
+ * One transaction under the conversation row lock (as `RunContextService`): compares each
141
+ * answer's hash with the plugin's latest `plugin_context` and appends the ones that differ (a
142
+ * plugin with no earlier event and no text writes nothing), plus an empty event for each plugin
143
+ * in `off` whose latest text is not empty. Idem key per run and plugin, so a retried run
144
+ * start finds its events or nothing changed. Returns the last seq written.
145
+ */
146
+ async append(input, texts, off) {
147
+ const t = this.db.db;
148
+ await t
149
+ .selectFrom("conversations")
150
+ .select("id")
151
+ .where("id", "=", input.conversationId)
152
+ .forUpdate()
153
+ .executeTakeFirst();
154
+ const rows = await t
155
+ .selectFrom("conversation_events")
156
+ .select(["seq", "payload"])
157
+ .where("conversation_id", "=", input.conversationId)
158
+ .where("type", "=", "plugin_context")
159
+ .orderBy("seq", "asc")
160
+ .execute();
161
+ const latest = latestPluginContexts(rows.map((r) => ({ type: "plugin_context", seq: Number(r.seq), payload: r.payload })), Number.POSITIVE_INFINITY);
162
+ const next = new Map(texts);
163
+ for (const [plugin, prev] of latest) {
164
+ if (off.has(plugin) && prev.text !== "")
165
+ next.set(plugin, "");
166
+ }
167
+ const changed = [...next]
168
+ .filter(([plugin, text]) => {
169
+ const prev = latest.get(plugin);
170
+ return prev === undefined ? text !== "" : prev.hash !== sha256(text);
171
+ })
172
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
173
+ if (changed.length === 0)
174
+ return null;
175
+ const written = await this.eventLog.appendIn(input.conversationId, changed.map(([plugin, text]) => ({
176
+ type: "plugin_context",
177
+ runId: input.runId,
178
+ idemKey: pluginContextIdemKey(input.runId, plugin),
179
+ payload: { plugin, text, hash: sha256(text) },
180
+ })));
181
+ return Math.max(...written.map((e) => e.seq));
182
+ }
183
+ };
184
+ __decorate([
185
+ Transactional()
186
+ ], PluginContextService.prototype, "append", null);
187
+ PluginContextService = __decorate([
188
+ Injectable(),
189
+ __param(0, Inject(KernelDb)),
190
+ __param(1, Inject(EventLog)),
191
+ __param(2, Inject(TransactionHost)),
192
+ __param(3, Inject(ExtensionPointDefaults)),
193
+ __param(4, Inject(ExtensionRegistry)),
194
+ __param(5, Inject(KERNEL_LOGGER))
195
+ ], PluginContextService);
196
+ export { PluginContextService };
197
+ /** `p`, or a rejection once `signal` aborts (a provider that ignores its signal never holds a run). */
198
+ function untilAborted(p, signal) {
199
+ return new Promise((resolve, reject) => {
200
+ const onAbort = () => reject(signal.reason);
201
+ if (signal.aborted) {
202
+ onAbort();
203
+ return;
204
+ }
205
+ signal.addEventListener("abort", onAbort, { once: true });
206
+ p.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
207
+ });
208
+ }