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

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 (234) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.controller.d.ts +3 -3
  3. package/dist/admin/admin.controller.js +6 -2
  4. package/dist/admin/admin.module.d.ts +2 -1
  5. package/dist/admin/admin.module.js +7 -3
  6. package/dist/admin/admin.schemas.d.ts +13 -0
  7. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  8. package/dist/admin/outbox-dead.controller.js +62 -0
  9. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  10. package/dist/admin/outbox-dead.schemas.js +18 -0
  11. package/dist/admin/outbox-dead.service.d.ts +66 -0
  12. package/dist/admin/outbox-dead.service.js +179 -0
  13. package/dist/admin/overview.service.d.ts +40 -5
  14. package/dist/admin/overview.service.js +41 -7
  15. package/dist/agents/from-template.js +1 -1
  16. package/dist/background/finish.d.ts +5 -2
  17. package/dist/background/finish.js +14 -3
  18. package/dist/background/launch.d.ts +44 -5
  19. package/dist/background/launch.js +92 -9
  20. package/dist/background/session.d.ts +14 -2
  21. package/dist/background/session.js +26 -4
  22. package/dist/background/status.d.ts +1 -1
  23. package/dist/background/status.js +3 -3
  24. package/dist/background/stop.d.ts +0 -2
  25. package/dist/background/stop.js +1 -21
  26. package/dist/background/store.d.ts +8 -0
  27. package/dist/background/store.js +24 -0
  28. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  29. package/dist/catalog/admin-catalog.controller.js +45 -6
  30. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  31. package/dist/catalog/admin-catalog.schemas.js +8 -0
  32. package/dist/catalog/catalog-content.d.ts +5 -0
  33. package/dist/catalog/catalog-content.js +1 -0
  34. package/dist/catalog/catalog.module.d.ts +2 -1
  35. package/dist/catalog/catalog.module.js +5 -1
  36. package/dist/catalog/marketplaces.service.d.ts +1 -1
  37. package/dist/catalog/platform-import-update.d.ts +50 -0
  38. package/dist/catalog/platform-import-update.js +116 -0
  39. package/dist/catalog/plugin-import.d.ts +36 -1
  40. package/dist/catalog/plugin-import.js +23 -1
  41. package/dist/catalog/plugin-upgrade.d.ts +18 -0
  42. package/dist/catalog/plugin-upgrade.js +19 -0
  43. package/dist/catalog/requests.service.js +2 -2
  44. package/dist/channels/channels.module.d.ts +5 -2
  45. package/dist/channels/channels.module.js +9 -2
  46. package/dist/channels/deliver-job.d.ts +25 -0
  47. package/dist/channels/deliver-job.js +25 -0
  48. package/dist/channels/inbound.d.ts +1 -1
  49. package/dist/channels/inbound.js +2 -15
  50. package/dist/channels/jobs.d.ts +28 -18
  51. package/dist/channels/jobs.js +63 -26
  52. package/dist/config/schema.d.ts +3 -6
  53. package/dist/config/schema.js +20 -27
  54. package/dist/conversations/agent-tree.d.ts +16 -0
  55. package/dist/conversations/agent-tree.js +36 -0
  56. package/dist/conversations/conversations.service.d.ts +44 -14
  57. package/dist/conversations/conversations.service.js +52 -38
  58. package/dist/conversations/event-log.d.ts +7 -10
  59. package/dist/conversations/event-log.js +24 -15
  60. package/dist/conversations/events.js +2 -1
  61. package/dist/conversations/queue.service.d.ts +8 -3
  62. package/dist/conversations/queue.service.js +37 -13
  63. package/dist/conversations/titles.d.ts +21 -8
  64. package/dist/conversations/titles.js +42 -15
  65. package/dist/db/kernel-schema.d.ts +26 -8
  66. package/dist/devices/devices.service.d.ts +12 -3
  67. package/dist/devices/devices.service.js +30 -9
  68. package/dist/errors.d.ts +15 -0
  69. package/dist/errors.js +15 -0
  70. package/dist/evals/run-replay.js +14 -8
  71. package/dist/extensions/extensions.module.js +3 -0
  72. package/dist/extensions/lifecycle.d.ts +6 -1
  73. package/dist/extensions/lifecycle.js +9 -2
  74. package/dist/extensions/usage-subscribers.d.ts +43 -0
  75. package/dist/extensions/usage-subscribers.js +108 -0
  76. package/dist/http/api.module.js +2 -0
  77. package/dist/identity/settings.d.ts +53 -3
  78. package/dist/identity/settings.js +92 -9
  79. package/dist/index.d.ts +1 -0
  80. package/dist/index.js +1 -0
  81. package/dist/infra/cli/kernel-cli.module.js +5 -0
  82. package/dist/infra/config/kernel-config.service.d.ts +7 -0
  83. package/dist/infra/config/kernel-config.service.js +20 -0
  84. package/dist/infra/events/kernel-events.d.ts +9 -35
  85. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  86. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  87. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  88. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  89. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  90. package/dist/infra/outbox/outbox.module.js +50 -0
  91. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  92. package/dist/infra/outbox/outbox.relay.js +76 -0
  93. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  94. package/dist/infra/outbox/outbox.service.js +131 -0
  95. package/dist/infra/outbox/registry.d.ts +120 -0
  96. package/dist/infra/outbox/registry.js +108 -0
  97. package/dist/infra/queue/queue-names.d.ts +8 -0
  98. package/dist/infra/queue/queue-names.js +16 -0
  99. package/dist/kernel.module.js +3 -0
  100. package/dist/local-devices/device-policy.service.d.ts +4 -0
  101. package/dist/local-devices/device-policy.service.js +61 -3
  102. package/dist/local-devices/local-device-gateway.d.ts +4 -2
  103. package/dist/local-devices/local-device-gateway.js +39 -14
  104. package/dist/local-devices/machines.d.ts +25 -1
  105. package/dist/local-devices/machines.js +91 -15
  106. package/dist/local-devices/policy-summary.d.ts +44 -1
  107. package/dist/local-devices/policy-summary.js +69 -0
  108. package/dist/models/embeddings.d.ts +9 -9
  109. package/dist/models/embeddings.js +11 -18
  110. package/dist/models/models.module.js +3 -0
  111. package/dist/orgs/orgs.service.js +7 -0
  112. package/dist/orgs/settings.controller.js +2 -2
  113. package/dist/orgs/settings.service.d.ts +11 -5
  114. package/dist/orgs/settings.service.js +28 -14
  115. package/dist/overrides.d.ts +20 -3
  116. package/dist/overrides.js +10 -2
  117. package/dist/platform/exclusions.js +5 -0
  118. package/dist/platform/ops/admin.ops.js +50 -9
  119. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  120. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  121. package/dist/platform/ops/org-settings.ops.js +3 -3
  122. package/dist/platform/ops/orgs.ops.js +1 -0
  123. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  124. package/dist/platform/ops/outbox.ops.js +125 -0
  125. package/dist/platform/ops/platform.ops.js +1 -1
  126. package/dist/platform/ops/schedules.ops.js +40 -1
  127. package/dist/platform/platform-ops.module.d.ts +2 -1
  128. package/dist/platform/platform-ops.module.js +3 -0
  129. package/dist/platform/route-schemas.js +3 -0
  130. package/dist/roles/worker.module.js +9 -4
  131. package/dist/runtime/langgraph-loop.js +13 -5
  132. package/dist/runtime/limits.d.ts +15 -0
  133. package/dist/runtime/limits.js +25 -0
  134. package/dist/runtime/loop.d.ts +8 -3
  135. package/dist/runtime/loop.js +2 -1
  136. package/dist/runtime/maintenance.d.ts +4 -2
  137. package/dist/runtime/maintenance.js +17 -2
  138. package/dist/runtime/model-call.d.ts +14 -16
  139. package/dist/runtime/model-call.js +14 -24
  140. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  141. package/dist/runtime/queue-lost-jobs.js +51 -0
  142. package/dist/runtime/run-control.d.ts +42 -5
  143. package/dist/runtime/run-control.js +66 -12
  144. package/dist/runtime/run-finish.d.ts +39 -12
  145. package/dist/runtime/run-finish.js +97 -14
  146. package/dist/runtime/run-jobs.d.ts +95 -13
  147. package/dist/runtime/run-jobs.js +199 -28
  148. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  149. package/dist/runtime/run-orphan-check.job.js +339 -0
  150. package/dist/runtime/run-processors.d.ts +16 -9
  151. package/dist/runtime/run-processors.js +1 -1
  152. package/dist/runtime/run-queue.d.ts +64 -55
  153. package/dist/runtime/run-queue.js +147 -284
  154. package/dist/runtime/run-steps.d.ts +1 -2
  155. package/dist/runtime/run-steps.js +8 -11
  156. package/dist/runtime/run-worker.d.ts +6 -17
  157. package/dist/runtime/run-worker.js +32 -34
  158. package/dist/runtime/runtime.module.d.ts +2 -3
  159. package/dist/runtime/runtime.module.js +5 -6
  160. package/dist/runtime/step-log.d.ts +16 -4
  161. package/dist/runtime/step-log.js +26 -6
  162. package/dist/runtime/stream.controller.d.ts +2 -4
  163. package/dist/runtime/stream.controller.js +5 -11
  164. package/dist/runtime/subagents/child.js +1 -1
  165. package/dist/runtime/subagents/definition.d.ts +2 -1
  166. package/dist/runtime/subagents/definition.js +4 -2
  167. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  168. package/dist/runtime/subagents/deliveries.js +16 -7
  169. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  170. package/dist/runtime/subagents/dispatch.js +13 -38
  171. package/dist/runtime/subagents/messages.d.ts +5 -1
  172. package/dist/runtime/subagents/messages.js +11 -3
  173. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  174. package/dist/runtime/subagents/skill-fork.js +5 -5
  175. package/dist/runtime/subagents/subagents.service.js +1 -1
  176. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  177. package/dist/runtime/subagents/tree-jobs.js +86 -0
  178. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  179. package/dist/runtime/usage-report-jobs.js +67 -119
  180. package/dist/runtime/usage-report.d.ts +28 -77
  181. package/dist/runtime/usage-report.js +35 -213
  182. package/dist/runtime/waits.d.ts +48 -198
  183. package/dist/runtime/waits.js +103 -805
  184. package/dist/sandbox/platform-tiers.d.ts +86 -0
  185. package/dist/sandbox/platform-tiers.js +97 -0
  186. package/dist/sandbox/sandbox.service.d.ts +18 -5
  187. package/dist/sandbox/sandbox.service.js +75 -20
  188. package/dist/sandbox/tiers.d.ts +14 -2
  189. package/dist/sandbox/tiers.js +8 -0
  190. package/dist/schedules/calendar.d.ts +134 -0
  191. package/dist/schedules/calendar.js +410 -0
  192. package/dist/schedules/fire.d.ts +1 -3
  193. package/dist/schedules/fire.js +5 -10
  194. package/dist/schedules/run-tracker.d.ts +0 -5
  195. package/dist/schedules/run-tracker.js +0 -13
  196. package/dist/schedules/scan.job.js +15 -20
  197. package/dist/schedules/schedule-math.d.ts +42 -0
  198. package/dist/schedules/schedule-math.js +52 -0
  199. package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
  200. package/dist/schedules/scheduled-tasks.service.js +53 -0
  201. package/dist/schedules/schedules.controller.d.ts +5 -1
  202. package/dist/schedules/schedules.controller.js +15 -1
  203. package/dist/schedules/schedules.schemas.d.ts +35 -0
  204. package/dist/schedules/schedules.schemas.js +27 -0
  205. package/dist/schedules/store.d.ts +5 -0
  206. package/dist/schedules/store.js +20 -0
  207. package/dist/tools/approvals.service.d.ts +26 -7
  208. package/dist/tools/approvals.service.js +88 -10
  209. package/dist/tools/builtin-names.d.ts +5 -3
  210. package/dist/tools/builtin-names.js +12 -4
  211. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  212. package/dist/tools/builtins/builtin-tools.js +21 -17
  213. package/dist/tools/builtins/sandbox/bash.js +9 -3
  214. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  215. package/dist/tools/builtins/sandbox/common.js +14 -1
  216. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  217. package/dist/tools/builtins/sandbox/index.js +4 -1
  218. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  219. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  220. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  221. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  222. package/dist/tools/builtins/web/extract.d.ts +2 -4
  223. package/dist/tools/builtins/web/extract.js +2 -5
  224. package/dist/tools/pipeline.d.ts +1 -1
  225. package/dist/tools/pipeline.js +1 -1
  226. package/dist/tools/questions.service.d.ts +15 -6
  227. package/dist/tools/questions.service.js +20 -8
  228. package/dist/tools/registry.d.ts +4 -2
  229. package/dist/tools/registry.js +4 -3
  230. package/dist/tools/tools.module.js +4 -3
  231. package/package.json +3 -3
  232. package/src/migrations/0118_device_policy_ops.sql +6 -0
  233. package/src/migrations/0119_outbox.sql +22 -0
  234. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
@@ -11,10 +11,12 @@ import { EXTENSION_NAME_RE } from "@capybara-hq/plugin-sdk";
11
11
  import { Inject, Injectable } from "@nestjs/common";
12
12
  import { z } from "zod";
13
13
  import { ApiError } from "../errors.js";
14
+ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
14
15
  import { KernelDb } from "../infra/database/kernel-db.js";
15
16
  import { TransactionHost } from "../infra/database/transaction-host.js";
16
17
  import { Transactional } from "../infra/database/transactional.decorator.js";
17
18
  import { KERNEL_LOGGER } from "../infra/tokens.js";
19
+ import { applyPlatformTiersPatch, chosenPlatformDefault, PLATFORM_TIER_FIELDS, PLATFORM_TIERS_PATCH, PLATFORM_TIERS_SETTING, platformTiersWiden, readStoredPlatformTiers, resolvePlatformTiers, } from "../sandbox/platform-tiers.js";
18
20
  import { assertPlatformDefaultEligible, lockThemeShared } from "../themes/theme-state.js";
19
21
  import { mergeArrangement, UiArrangementPatchSchema, UiArrangementSchema, } from "../ui/arrangement.js";
20
22
  import { audit } from "./audit.js";
@@ -153,6 +155,7 @@ export const DEPLOYMENT_SETTINGS = z.object({
153
155
  clientExtensions: ClientExtensionsSetting.default(clientExtensionsDefault),
154
156
  });
155
157
  const DEFAULTS = DEPLOYMENT_SETTINGS.parse({});
158
+ const tierWidenRefused = () => new ApiError("deployment.sandbox_tier_not_allowed", "Raising the platform's sandbox level (sandboxTiers) needs a super admin in the admin console; an API token or the Platform tool may only lower it.");
156
159
  /**
157
160
  * 设置的修改(PUT /api/v1/admin/settings 的 body):只改给出的键。
158
161
  * `runConcurrencyOverrides` 按组织合并:值为正整数 → 设置该组织的上限;值为 null → 删除该组织的覆盖
@@ -198,6 +201,14 @@ export const DEPLOYMENT_SETTINGS_PATCH = z
198
201
  message: "give enabled, disabled or both",
199
202
  })
200
203
  .optional(),
204
+ /**
205
+ * The platform's sandbox tiers (sandbox dev tiers spec §4.1): each given field replaces the
206
+ * stored one, null clears it (back to the default: max dev, default dev, unsandboxed allowed).
207
+ * A field set in the startup config is read-only (409 `deployment.setting_readonly`); the
208
+ * default may not be above the max; an API token or the Platform tool may only narrow (400
209
+ * `deployment.sandbox_tier_not_allowed`).
210
+ */
211
+ sandboxTiers: PLATFORM_TIERS_PATCH.optional(),
201
212
  })
202
213
  .strict();
203
214
  /**
@@ -350,11 +361,66 @@ let SettingsService = class SettingsService {
350
361
  txHost;
351
362
  adminLock;
352
363
  log;
353
- constructor(db, txHost, adminLock, log) {
364
+ config;
365
+ constructor(db, txHost, adminLock, log,
366
+ /** The startup config's `sandbox.tiers`, which wins over the stored choice. */
367
+ config) {
354
368
  this.db = db;
355
369
  this.txHost = txHost;
356
370
  this.adminLock = adminLock;
357
371
  this.log = log;
372
+ this.config = config;
373
+ }
374
+ /** The settings with the platform's sandbox tiers in effect and their sources. */
375
+ async view() {
376
+ const [settings, stored] = await Promise.all([
377
+ this.get(),
378
+ readStoredPlatformTiers(this.db.db, this.log),
379
+ ]);
380
+ return { ...settings, sandboxTiers: this.tiersView(stored) };
381
+ }
382
+ tiersView(stored) {
383
+ const config = this.config.get().sandbox.tiers;
384
+ const sources = Object.fromEntries(PLATFORM_TIER_FIELDS.map((f) => [
385
+ f,
386
+ this.config.fieldSource(`sandbox.tiers.${f}`) ??
387
+ (stored[f] !== undefined ? "db" : "default"),
388
+ ]));
389
+ return { ...resolvePlatformTiers(config, stored), sources };
390
+ }
391
+ /**
392
+ * The refusals of a `sandboxTiers` change against the stored choice: a field set in the
393
+ * startup config (`deployment.setting_readonly`), a default above the max
394
+ * (`validation_failed`), and with `onlyNarrows` (an API token, the Platform tool) any widening:
395
+ * a higher max or default, unsandboxed allowed, in effect or in the stored choice (which
396
+ * applies once the startup config stops setting a field). Returns the next stored choice and
397
+ * the effective tiers before and after.
398
+ */
399
+ async checkSandboxTiers(patch, opts = {}) {
400
+ const readonly = PLATFORM_TIER_FIELDS.filter((f) => patch[f] !== undefined && this.config.fieldSource(`sandbox.tiers.${f}`) !== undefined);
401
+ if (readonly.length > 0) {
402
+ throw new ApiError("deployment.setting_readonly", `These sandbox tier settings are set in the startup config (file, Nacos or environment) and are read-only here: ${readonly.map((f) => `sandbox.tiers.${f}`).join(", ")}`, readonly.map((f) => ({
403
+ path: `sandboxTiers.${f}`,
404
+ source: this.config.fieldSource(`sandbox.tiers.${f}`),
405
+ })));
406
+ }
407
+ const config = this.config.get().sandbox.tiers;
408
+ const stored = await readStoredPlatformTiers(this.db.db, this.log);
409
+ const next = applyPlatformTiersPatch(stored, patch);
410
+ const before = resolvePlatformTiers(config, stored);
411
+ const after = resolvePlatformTiers(config, next);
412
+ const chosen = chosenPlatformDefault(config, next);
413
+ if ((patch.max !== undefined || patch.default !== undefined) && chosen !== after.default) {
414
+ throw new ApiError("validation_failed", `The default sandbox tier (${chosen}) cannot be above the highest (${after.max}); lower sandboxTiers.default as well`, [{ path: "sandboxTiers.default", message: `must be at most ${after.max}` }]);
415
+ }
416
+ if (opts.onlyNarrows) {
417
+ const none = {};
418
+ if (platformTiersWiden(before, after) ||
419
+ platformTiersWiden(resolvePlatformTiers(none, stored), resolvePlatformTiers(none, next))) {
420
+ throw tierWidenRefused();
421
+ }
422
+ }
423
+ return { next, before, after };
358
424
  }
359
425
  /**
360
426
  * Reads every setting, in the context's transaction when there is one (a caller holding a
@@ -382,18 +448,21 @@ let SettingsService = class SettingsService {
382
448
  }
383
449
  return out;
384
450
  }
385
- /** Updates settings under the super admin lock, re-checking the actor (else `forbidden`). */
451
+ /**
452
+ * Updates settings under the super admin lock, re-checking the actor (else `forbidden`).
453
+ * `onlyNarrows` (an API token, the Platform tool): `sandboxTiers` may only narrow.
454
+ */
386
455
  async update(patch, actorAccountId, actorTokenId,
387
456
  /** Merged into the audit detail (the Platform tool's `via: "agent"`). */
388
- auditDetail) {
457
+ auditDetail, opts = {}) {
389
458
  const check = DEPLOYMENT_SETTINGS_PATCH.safeParse(patch);
390
459
  if (!check.success) {
391
460
  throw new ApiError("validation_failed", "Invalid deployment settings", check.error.issues.map((i) => ({ path: i.path.join("."), message: i.message })));
392
461
  }
393
- await this.write(check.data, actorAccountId, actorTokenId, auditDetail);
394
- return this.get();
462
+ await this.write(check.data, actorAccountId, actorTokenId, auditDetail, opts);
463
+ return this.view();
395
464
  }
396
- async write(patch, actorAccountId, actorTokenId, auditDetail) {
465
+ async write(patch, actorAccountId, actorTokenId, auditDetail, opts) {
397
466
  await this.adminLock.lockAndAuthorize(actorAccountId);
398
467
  // The super admin lock serializes every settings write: the current overrides are read
399
468
  // and merged under it, so no concurrent change is lost
@@ -452,7 +521,16 @@ let SettingsService = class SettingsService {
452
521
  next.subagentLimitOverrides = mergeByOrg(current.subagentLimitOverrides, subagentPatch);
453
522
  }
454
523
  }
455
- for (const [key, value] of Object.entries(next)) {
524
+ // the platform's sandbox tiers: checked against the stored choice under the lock; lowering
525
+ // the max needs no org rewrite, the effective policy cuts every org's ceiling and default
526
+ let tiersChange;
527
+ const writes = Object.entries(next);
528
+ if (patch.sandboxTiers !== undefined) {
529
+ const r = await this.checkSandboxTiers(patch.sandboxTiers, opts);
530
+ tiersChange = { before: r.before, after: r.after };
531
+ writes.push([PLATFORM_TIERS_SETTING, r.next]);
532
+ }
533
+ for (const [key, value] of writes) {
456
534
  if (value === undefined)
457
535
  continue;
458
536
  const json = JSON.stringify(value);
@@ -468,7 +546,11 @@ let SettingsService = class SettingsService {
468
546
  actorAccountId,
469
547
  ...(actorTokenId !== undefined && { actorTokenId }),
470
548
  action: "deployment.settings.updated",
471
- detail: { ...patch, ...auditDetail },
549
+ detail: {
550
+ ...patch,
551
+ ...(tiersChange !== undefined && { sandboxTiersEffective: tiersChange }),
552
+ ...auditDetail,
553
+ },
472
554
  });
473
555
  }
474
556
  };
@@ -480,6 +562,7 @@ SettingsService = __decorate([
480
562
  __param(0, Inject(KernelDb)),
481
563
  __param(1, Inject(TransactionHost)),
482
564
  __param(2, Inject(SuperAdminLock)),
483
- __param(3, Inject(KERNEL_LOGGER))
565
+ __param(3, Inject(KERNEL_LOGGER)),
566
+ __param(4, Inject(KernelConfigService))
484
567
  ], SettingsService);
485
568
  export { SettingsService };
package/dist/index.d.ts CHANGED
@@ -70,6 +70,7 @@ export { ROUTE_SCHEMAS, type RouteSchemas } from "./platform/route-schemas.js";
70
70
  export { UNKNOWN_RESULT_TEXT } from "./runtime/context/pairing.js";
71
71
  export type { AgentLoop, LoopContext, LoopOutcome } from "./runtime/loop.js";
72
72
  export { MAINTENANCE_QUEUE_NAME } from "./runtime/maintenance-queue.js";
73
+ export { type OrphanCandidate, queuedRunsWithoutJob } from "./runtime/run-orphan-check.job.js";
73
74
  export { runJobId } from "./runtime/run-queue.js";
74
75
  export { SANDBOX_DESTROY_JOB, SANDBOX_SUSPEND_JOB, } from "./sandbox/jobs.js";
75
76
  export { SANDBOX_LOCK_TTL_MS, SandboxService, type SandboxUseInfo, } from "./sandbox/sandbox.service.js";
package/dist/index.js CHANGED
@@ -68,6 +68,7 @@ export { PlatformOperations } from "./platform/registry.js";
68
68
  export { ROUTE_SCHEMAS } from "./platform/route-schemas.js";
69
69
  export { UNKNOWN_RESULT_TEXT } from "./runtime/context/pairing.js";
70
70
  export { MAINTENANCE_QUEUE_NAME } from "./runtime/maintenance-queue.js";
71
+ export { queuedRunsWithoutJob } from "./runtime/run-orphan-check.job.js";
71
72
  export { runJobId } from "./runtime/run-queue.js";
72
73
  export { SANDBOX_DESTROY_JOB, SANDBOX_SUSPEND_JOB, } from "./sandbox/jobs.js";
73
74
  export { SANDBOX_LOCK_TTL_MS, SandboxService, } from "./sandbox/sandbox.service.js";
@@ -21,6 +21,7 @@ import { KernelEventsModule } from "../events/kernel-events.js";
21
21
  import { GracefulShutdownModule } from "../lifecycle/graceful-shutdown.module.js";
22
22
  import { LockModule } from "../lock/lock.module.js";
23
23
  import { LoggingModule } from "../logging/logging.module.js";
24
+ import { OutboxModule } from "../outbox/outbox.module.js";
24
25
  import { QueueModule } from "../queue/queue.module.js";
25
26
  import { KERNEL_BUS, KERNEL_LOGGER, REDIS, REDIS_SUBSCRIBER } from "../tokens.js";
26
27
  /** How long the CLI's Redis connection may take before a command fails (a publish only warns). */
@@ -110,6 +111,10 @@ let KernelCliModule = KernelCliModule_1 = class KernelCliModule {
110
111
  ConfigModule.forRoot(config, opts.env !== undefined ? { env: opts.env } : {}),
111
112
  GracefulShutdownModule,
112
113
  DatabaseModule.forRoot(),
114
+ // owed jobs a command's domain writes carry (an extension's embed call owes its usage
115
+ // report); delivered by the fast path when the command's context has the queue, else by a
116
+ // worker's relay
117
+ OutboxModule,
113
118
  // QueueModule's root gives the queues a command's modules register (the catalog's scope
114
119
  // sync and maintenance queues) the configured Redis; without it BullMQ would connect to
115
120
  // its default 127.0.0.1:6379
@@ -15,6 +15,13 @@ export declare class KernelConfigService {
15
15
  private readonly listeners;
16
16
  constructor(input: KernelConfigInput, log: KernelLogger);
17
17
  get(): KernelConfig;
18
+ /**
19
+ * Where the current value of a field without a schema default comes from: `env` when an
20
+ * environment variable sets it, else the config source (`file` / `nacos`) when it is set at all;
21
+ * undefined when nothing sets it (a layer below the startup config decides, e.g. the deployment
22
+ * settings for `sandbox.tiers.*`). Such fields are read-only in the admin console.
23
+ */
24
+ fieldSource(path: string): "env" | "file" | "nacos" | undefined;
18
25
  /** Subscribes to applied changes (hot fields only); returns the unsubscribe function. */
19
26
  onChange(listener: ConfigChangeListener): () => void;
20
27
  /**
@@ -8,9 +8,11 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { Inject, Injectable } from "@nestjs/common";
11
+ import { envOverrides } from "../../config/env.js";
11
12
  import { changedPaths, configFields, getPath, secretValues, setPath } from "../../config/fields.js";
12
13
  import { resolveConfig } from "../../config/load.js";
13
14
  import { KernelConfigSchema } from "../../config/schema.js";
15
+ import { configSourceKind } from "../../config/sources.js";
14
16
  import { KERNEL_CONFIG_INPUT, KERNEL_LOGGER } from "../tokens.js";
15
17
  /**
16
18
  * Appliers of the hot-reloadable fields: the key is a dotted path marked `hot` in the schema. A
@@ -101,6 +103,24 @@ let KernelConfigService = class KernelConfigService {
101
103
  get() {
102
104
  return this.current;
103
105
  }
106
+ /**
107
+ * Where the current value of a field without a schema default comes from: `env` when an
108
+ * environment variable sets it, else the config source (`file` / `nacos`) when it is set at all;
109
+ * undefined when nothing sets it (a layer below the startup config decides, e.g. the deployment
110
+ * settings for `sandbox.tiers.*`). Such fields are read-only in the admin console.
111
+ */
112
+ fieldSource(path) {
113
+ if (getPath(this.current, path) === undefined)
114
+ return undefined;
115
+ let fromEnv = false;
116
+ try {
117
+ fromEnv = getPath(envOverrides(this.env), path) !== undefined;
118
+ }
119
+ catch {
120
+ // conflicting variables fail the start; a later change here only loses the label
121
+ }
122
+ return fromEnv ? "env" : configSourceKind(this.env);
123
+ }
104
124
  /** Subscribes to applied changes (hot fields only); returns the unsubscribe function. */
105
125
  onChange(listener) {
106
126
  this.listeners.add(listener);
@@ -23,8 +23,9 @@ export interface KernelEventMap {
23
23
  };
24
24
  /**
25
25
  * A person decided a tool call approval (after the commit, exactly once: only the decision that
26
- * changed it from pending). The run's waits resume it and its IM delivery is enqueued; a lost
27
- * event is re-derived by the wait sweep (runs waiting on a decided approval are resumed).
26
+ * changed it from pending). The decision's transaction resumed the run and, for a conversation
27
+ * with a channel target, owed the IM delivery that updates the card (with its `approval_decided`
28
+ * append); the event itself is only the decision's announcement.
28
29
  */
29
30
  "approval.decided": {
30
31
  id: string;
@@ -36,8 +37,7 @@ export interface KernelEventMap {
36
37
  };
37
38
  /**
38
39
  * A person answered a tool call's input request, or skipped it with a message (after the commit,
39
- * exactly once). Same
40
- * consumers and the same re-derivation as `approval.decided`.
40
+ * exactly once). The same transaction contract as `approval.decided`.
41
41
  */
42
42
  "question.answered": {
43
43
  id: string;
@@ -47,38 +47,20 @@ export interface KernelEventMap {
47
47
  /** `skipped`: the run's starter sent a message instead (reply-while-question spec §6.3). */
48
48
  outcome: "answered" | "skipped";
49
49
  };
50
- /**
51
- * EventAppended: a conversation's event log advanced to `toSeq` (after the commit, from
52
- * `EventLog.notify`, right after the bus publish). The channel outbound delivery enqueues a job
53
- * for a conversation with an IM target; a lost event is re-derived by the next append's (and the
54
- * finished delivery job re-checks the log for content it has not delivered).
55
- */
56
- "conversation.event-appended": {
57
- conversationId: string;
58
- toSeq: number;
59
- };
60
50
  /**
61
51
  * A run started executing on this replica (its first start, a resume, or a takeover after a
62
52
  * stall), after it took the conversation lock and an org quota slot. The channel outbound
63
- * enqueues the conversation's IM delivery; a lost event is re-derived by the run's next append.
53
+ * enqueues the conversation's IM delivery at once (a hint: the run's appends owe it, transactional
54
+ * outbox spec §8.1).
64
55
  */
65
56
  "run.started": {
66
57
  conversationId: string;
67
58
  runId: string;
68
59
  };
69
60
  /**
70
- * A stop of a run waiting on a person was handled (`RunControl.abortWaiting`, whether or not it
71
- * still waited): its approval and question cards change, so the channel outbound enqueues the
72
- * conversation's IM delivery (re-derived by the next append when lost).
73
- */
74
- "run.wait-stopped": {
75
- conversationId: string;
76
- runId: string;
77
- };
78
- /**
79
- * `ensureRun` marked an orphaned run failed (after the commit): the waits stop its active fork
80
- * sub-agent at once (background sub-agents keep running, agent map spec §5.4). A lost event is
81
- * repaired by the wait sweep (fork children of finished runs).
61
+ * The `run-orphan-check` failed an orphaned run (after the commit): the waits stop its active
62
+ * fork sub-agent at once (background sub-agents keep running, agent map spec §5.4). A lost event
63
+ * is repaired by the `stop-tree` job the run's terminal transaction owed.
82
64
  */
83
65
  "run.orphaned": {
84
66
  runId: string;
@@ -99,14 +81,6 @@ export interface KernelEventMap {
99
81
  "background_task.finished": {
100
82
  conversationId: string;
101
83
  };
102
- /**
103
- * An extension's `ctx.models.embed` call wrote a `model_usage` row (after the commit, spec B
104
- * §5.4). The usage reporter enqueues its `usage.report` delivery; a lost event is re-derived by
105
- * the usage sweep (rows whose `reported_at` is still null).
106
- */
107
- "model_usage.recorded": {
108
- id: string;
109
- };
110
84
  }
111
85
  type Handler<K extends keyof KernelEventMap> = (payload: KernelEventMap[K]) => Promise<void> | void;
112
86
  /** Typed in-process event emitter; handlers run in order, a failing one is logged and skipped. */
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The outbox relay (`@LockedInterval`), imported only by `WorkerModule`. Its dispatcher comes from
3
+ * the global `OutboxModule`, which every role has (the fast path runs everywhere).
4
+ */
5
+ export declare class OutboxRelayModule {
6
+ }
@@ -0,0 +1,18 @@
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
+ import { Module } from "@nestjs/common";
8
+ import { OutboxRelay } from "./outbox.relay.js";
9
+ /**
10
+ * The outbox relay (`@LockedInterval`), imported only by `WorkerModule`. Its dispatcher comes from
11
+ * the global `OutboxModule`, which every role has (the fast path runs everywhere).
12
+ */
13
+ let OutboxRelayModule = class OutboxRelayModule {
14
+ };
15
+ OutboxRelayModule = __decorate([
16
+ Module({ providers: [OutboxRelay] })
17
+ ], OutboxRelayModule);
18
+ export { OutboxRelayModule };
@@ -0,0 +1,103 @@
1
+ import { ModuleRef } from "@nestjs/core";
2
+ import type { Queue } from "bullmq";
3
+ import type { KernelLogger } from "../../log.js";
4
+ import { KernelDb } from "../database/kernel-db.js";
5
+ import { TransactionHost } from "../database/transaction-host.js";
6
+ import { type KernelQueueName } from "../queue/queue-names.js";
7
+ /**
8
+ * Retry schedule of a row whose add failed (spec §3.4): the next attempt waits
9
+ * `min(capMs, baseMs * 2^attempts)`, and the row is dead (kept, never retried) at `maxAttempts`.
10
+ */
11
+ export declare const OUTBOX_BACKOFF: {
12
+ readonly baseMs: 1000;
13
+ readonly capMs: 300000;
14
+ readonly maxAttempts: 20;
15
+ };
16
+ /**
17
+ * How long the queue adds of one claim pass may take in all: the claim transaction holds its rows'
18
+ * locks and a pool connection for at most about this long.
19
+ */
20
+ export declare const OUTBOX_REDIS_TIMEOUT_MS = 2000;
21
+ /**
22
+ * Most fast-path passes running at once in one process; more are skipped and left to the relay,
23
+ * so a slow Redis cannot take the whole pool (a local resource bound, no shared state).
24
+ */
25
+ export declare const OUTBOX_FAST_PATH_CONCURRENCY = 4;
26
+ /**
27
+ * Test seam (`KernelOverride`): returns true to make the adds to the given queue fail (a Redis
28
+ * outage) without calling Redis. The kernel never provides it; the dispatcher defaults to "never
29
+ * fails".
30
+ */
31
+ export declare const OUTBOX_DISPATCH_FAILS: unique symbol;
32
+ /** What one pass did: rows delivered, rows whose add failed, and of those the rows now dead. */
33
+ export interface DispatchResult {
34
+ sent: number;
35
+ failed: number;
36
+ dead: number;
37
+ }
38
+ /** The queue handle the dispatcher uses: adds, and the connection's state. */
39
+ export type OutboxQueue = Pick<Queue, "addBulk" | "getBackend">;
40
+ /**
41
+ * Delivers outbox rows to BullMQ (transactional outbox spec §3.3, §3.4). A pass claims rows
42
+ * `FOR UPDATE SKIP LOCKED` (so concurrent passes never take the same row), adds their jobs per
43
+ * queue with `addBulk` (the queue defaults overlaid by the registry's options of the job name, the
44
+ * row's job id, a future `run_at` as the remaining `delay`), and deletes
45
+ * the delivered rows in the same short transaction. A crash after the add and before the commit
46
+ * leaves the rows; the next pass adds again and BullMQ ignores the existing job id. A queue whose
47
+ * add fails keeps its rows, and a second short update records the attempt with exponential
48
+ * backoff, setting `dead_at` at the last one.
49
+ *
50
+ * The fast path never queues behind an outage: it skips the pass (the relay delivers the rows) when
51
+ * a queue's Redis connection is not ready or {@link OUTBOX_FAST_PATH_CONCURRENCY} passes already
52
+ * run. Within a pass, the first failed queue fails the queues after it without trying them.
53
+ *
54
+ * The queue handles are the instances the queues' owner modules registered (one per queue, no
55
+ * connection of the outbox's own), resolved by token at use time. A queue whose owner this
56
+ * process's roles do not load is not reachable here: no pass claims its rows, which stay for a
57
+ * process that has it.
58
+ */
59
+ export declare class OutboxDispatcher {
60
+ readonly txHost: TransactionHost;
61
+ private readonly db;
62
+ private readonly log;
63
+ private readonly moduleRef;
64
+ private readonly dispatchFails;
65
+ /** The owners' queue instances resolved so far (a per-process handle cache). */
66
+ private readonly queues;
67
+ /** Fast-path passes running in this process (bounded by OUTBOX_FAST_PATH_CONCURRENCY). */
68
+ private fastPaths;
69
+ constructor(txHost: TransactionHost, db: KernelDb, log: KernelLogger, moduleRef: Pick<ModuleRef, "get">, dispatchFails?: (queue: KernelQueueName) => boolean);
70
+ /** The queue instance its owner module registered, or undefined when no loaded module has it. */
71
+ private queue;
72
+ /** The kernel queues this process can add to, with their handles. */
73
+ private reachable;
74
+ /**
75
+ * The fast path: delivers the given rows (a committed transaction's own) right away. Never
76
+ * throws: whatever it does not deliver stays for the relay, and a failure is logged.
77
+ */
78
+ dispatchOwn(ids: readonly string[]): Promise<void>;
79
+ /**
80
+ * Every reachable queue's connection is ready (they reach the same Redis; the fast path does not
81
+ * read its rows' queues before claiming them).
82
+ */
83
+ private redisReady;
84
+ /**
85
+ * The relay: delivers up to `limit` due rows (not dead, `next_attempt_at` reached), oldest
86
+ * first.
87
+ */
88
+ dispatchDue(limit: number): Promise<DispatchResult>;
89
+ private dispatch;
90
+ /**
91
+ * One claim transaction: locks the rows (skipping rows another pass holds), adds their jobs and
92
+ * deletes the delivered rows. The only Redis calls made while a PG transaction is open in the
93
+ * kernel (spec §3.3): the locks held are outbox rows only, and the pass's adds together are
94
+ * bounded by {@link OUTBOX_REDIS_TIMEOUT_MS}.
95
+ */
96
+ private claimAndSend;
97
+ /**
98
+ * Records a failed add on its rows (after the claim transaction, so the attempt is kept whatever
99
+ * that transaction did): one more attempt, the error, the next attempt by {@link OUTBOX_BACKOFF},
100
+ * and `dead_at` at the last attempt. Returns how many rows it made dead.
101
+ */
102
+ private recordFailure;
103
+ }