@capybara-hq/kernel 0.1.0-rc.23 → 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 (206) 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 +7 -3
  4. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  5. package/dist/admin/outbox-dead.controller.js +62 -0
  6. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  7. package/dist/admin/outbox-dead.schemas.js +18 -0
  8. package/dist/admin/outbox-dead.service.d.ts +66 -0
  9. package/dist/admin/outbox-dead.service.js +179 -0
  10. package/dist/admin/overview.service.d.ts +40 -5
  11. package/dist/admin/overview.service.js +41 -7
  12. package/dist/agents/from-template.js +1 -1
  13. package/dist/background/finish.d.ts +5 -2
  14. package/dist/background/finish.js +14 -3
  15. package/dist/background/launch.d.ts +2 -1
  16. package/dist/background/launch.js +5 -2
  17. package/dist/background/store.d.ts +8 -0
  18. package/dist/background/store.js +24 -0
  19. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  20. package/dist/catalog/admin-catalog.controller.js +45 -6
  21. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  22. package/dist/catalog/admin-catalog.schemas.js +8 -0
  23. package/dist/catalog/catalog.module.d.ts +2 -1
  24. package/dist/catalog/catalog.module.js +5 -1
  25. package/dist/catalog/marketplaces.service.d.ts +1 -1
  26. package/dist/catalog/platform-import-update.d.ts +50 -0
  27. package/dist/catalog/platform-import-update.js +116 -0
  28. package/dist/catalog/plugin-import.d.ts +36 -1
  29. package/dist/catalog/plugin-import.js +23 -1
  30. package/dist/catalog/plugin-upgrade.d.ts +18 -0
  31. package/dist/catalog/plugin-upgrade.js +19 -0
  32. package/dist/channels/channels.module.d.ts +5 -2
  33. package/dist/channels/channels.module.js +9 -2
  34. package/dist/channels/deliver-job.d.ts +25 -0
  35. package/dist/channels/deliver-job.js +25 -0
  36. package/dist/channels/inbound.d.ts +1 -1
  37. package/dist/channels/inbound.js +2 -15
  38. package/dist/channels/jobs.d.ts +28 -18
  39. package/dist/channels/jobs.js +63 -26
  40. package/dist/config/schema.d.ts +0 -3
  41. package/dist/config/schema.js +11 -20
  42. package/dist/conversations/agent-tree.d.ts +16 -0
  43. package/dist/conversations/agent-tree.js +36 -0
  44. package/dist/conversations/conversations.service.d.ts +44 -14
  45. package/dist/conversations/conversations.service.js +52 -38
  46. package/dist/conversations/event-log.d.ts +7 -10
  47. package/dist/conversations/event-log.js +24 -15
  48. package/dist/conversations/events.js +2 -1
  49. package/dist/conversations/queue.service.d.ts +8 -3
  50. package/dist/conversations/queue.service.js +37 -13
  51. package/dist/conversations/titles.d.ts +21 -8
  52. package/dist/conversations/titles.js +42 -15
  53. package/dist/db/kernel-schema.d.ts +26 -8
  54. package/dist/devices/devices.service.d.ts +12 -3
  55. package/dist/devices/devices.service.js +18 -3
  56. package/dist/errors.d.ts +11 -0
  57. package/dist/errors.js +11 -0
  58. package/dist/evals/run-replay.js +14 -8
  59. package/dist/extensions/extensions.module.js +3 -0
  60. package/dist/extensions/lifecycle.d.ts +6 -1
  61. package/dist/extensions/lifecycle.js +9 -2
  62. package/dist/extensions/usage-subscribers.d.ts +43 -0
  63. package/dist/extensions/usage-subscribers.js +108 -0
  64. package/dist/http/api.module.js +2 -0
  65. package/dist/index.d.ts +1 -0
  66. package/dist/index.js +1 -0
  67. package/dist/infra/cli/kernel-cli.module.js +5 -0
  68. package/dist/infra/events/kernel-events.d.ts +9 -35
  69. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  70. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  71. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  72. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  73. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  74. package/dist/infra/outbox/outbox.module.js +50 -0
  75. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  76. package/dist/infra/outbox/outbox.relay.js +76 -0
  77. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  78. package/dist/infra/outbox/outbox.service.js +131 -0
  79. package/dist/infra/outbox/registry.d.ts +120 -0
  80. package/dist/infra/outbox/registry.js +108 -0
  81. package/dist/infra/queue/queue-names.d.ts +8 -0
  82. package/dist/infra/queue/queue-names.js +16 -0
  83. package/dist/kernel.module.js +3 -0
  84. package/dist/local-devices/device-policy.service.d.ts +4 -0
  85. package/dist/local-devices/device-policy.service.js +61 -3
  86. package/dist/local-devices/machines.d.ts +13 -5
  87. package/dist/local-devices/machines.js +45 -7
  88. package/dist/local-devices/policy-summary.d.ts +44 -1
  89. package/dist/local-devices/policy-summary.js +69 -0
  90. package/dist/models/embeddings.d.ts +9 -9
  91. package/dist/models/embeddings.js +11 -18
  92. package/dist/models/models.module.js +3 -0
  93. package/dist/orgs/orgs.service.js +7 -0
  94. package/dist/overrides.d.ts +20 -3
  95. package/dist/overrides.js +10 -2
  96. package/dist/platform/exclusions.js +5 -0
  97. package/dist/platform/ops/admin.ops.js +18 -2
  98. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  99. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  100. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  101. package/dist/platform/ops/outbox.ops.js +125 -0
  102. package/dist/platform/ops/schedules.ops.js +40 -1
  103. package/dist/platform/platform-ops.module.d.ts +2 -1
  104. package/dist/platform/platform-ops.module.js +3 -0
  105. package/dist/platform/route-schemas.js +3 -0
  106. package/dist/roles/worker.module.js +9 -4
  107. package/dist/runtime/langgraph-loop.js +13 -5
  108. package/dist/runtime/limits.d.ts +15 -0
  109. package/dist/runtime/limits.js +25 -0
  110. package/dist/runtime/loop.d.ts +8 -3
  111. package/dist/runtime/loop.js +2 -1
  112. package/dist/runtime/maintenance.d.ts +4 -2
  113. package/dist/runtime/maintenance.js +17 -2
  114. package/dist/runtime/model-call.d.ts +14 -16
  115. package/dist/runtime/model-call.js +14 -24
  116. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  117. package/dist/runtime/queue-lost-jobs.js +51 -0
  118. package/dist/runtime/run-control.d.ts +42 -5
  119. package/dist/runtime/run-control.js +66 -12
  120. package/dist/runtime/run-finish.d.ts +39 -12
  121. package/dist/runtime/run-finish.js +97 -14
  122. package/dist/runtime/run-jobs.d.ts +95 -13
  123. package/dist/runtime/run-jobs.js +199 -28
  124. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  125. package/dist/runtime/run-orphan-check.job.js +339 -0
  126. package/dist/runtime/run-processors.d.ts +16 -9
  127. package/dist/runtime/run-processors.js +1 -1
  128. package/dist/runtime/run-queue.d.ts +64 -55
  129. package/dist/runtime/run-queue.js +147 -284
  130. package/dist/runtime/run-steps.d.ts +1 -2
  131. package/dist/runtime/run-steps.js +8 -11
  132. package/dist/runtime/run-worker.d.ts +6 -17
  133. package/dist/runtime/run-worker.js +32 -34
  134. package/dist/runtime/runtime.module.d.ts +2 -3
  135. package/dist/runtime/runtime.module.js +5 -6
  136. package/dist/runtime/step-log.d.ts +16 -4
  137. package/dist/runtime/step-log.js +26 -6
  138. package/dist/runtime/stream.controller.d.ts +2 -4
  139. package/dist/runtime/stream.controller.js +5 -11
  140. package/dist/runtime/subagents/child.js +1 -1
  141. package/dist/runtime/subagents/definition.d.ts +2 -1
  142. package/dist/runtime/subagents/definition.js +4 -2
  143. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  144. package/dist/runtime/subagents/deliveries.js +16 -7
  145. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  146. package/dist/runtime/subagents/dispatch.js +13 -38
  147. package/dist/runtime/subagents/messages.d.ts +5 -1
  148. package/dist/runtime/subagents/messages.js +11 -3
  149. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  150. package/dist/runtime/subagents/skill-fork.js +5 -5
  151. package/dist/runtime/subagents/subagents.service.js +1 -1
  152. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  153. package/dist/runtime/subagents/tree-jobs.js +86 -0
  154. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  155. package/dist/runtime/usage-report-jobs.js +67 -119
  156. package/dist/runtime/usage-report.d.ts +28 -77
  157. package/dist/runtime/usage-report.js +35 -213
  158. package/dist/runtime/waits.d.ts +48 -198
  159. package/dist/runtime/waits.js +103 -805
  160. package/dist/sandbox/sandbox.service.d.ts +17 -4
  161. package/dist/sandbox/sandbox.service.js +55 -14
  162. package/dist/schedules/calendar.d.ts +134 -0
  163. package/dist/schedules/calendar.js +410 -0
  164. package/dist/schedules/fire.d.ts +1 -3
  165. package/dist/schedules/fire.js +5 -10
  166. package/dist/schedules/run-tracker.d.ts +0 -5
  167. package/dist/schedules/run-tracker.js +0 -13
  168. package/dist/schedules/scan.job.js +15 -20
  169. package/dist/schedules/schedule-math.d.ts +42 -0
  170. package/dist/schedules/schedule-math.js +52 -0
  171. package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
  172. package/dist/schedules/scheduled-tasks.service.js +53 -0
  173. package/dist/schedules/schedules.controller.d.ts +5 -1
  174. package/dist/schedules/schedules.controller.js +15 -1
  175. package/dist/schedules/schedules.schemas.d.ts +35 -0
  176. package/dist/schedules/schedules.schemas.js +27 -0
  177. package/dist/schedules/store.d.ts +5 -0
  178. package/dist/schedules/store.js +20 -0
  179. package/dist/tools/approvals.service.d.ts +26 -7
  180. package/dist/tools/approvals.service.js +88 -10
  181. package/dist/tools/builtin-names.d.ts +5 -3
  182. package/dist/tools/builtin-names.js +12 -4
  183. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  184. package/dist/tools/builtins/builtin-tools.js +21 -17
  185. package/dist/tools/builtins/sandbox/bash.js +9 -3
  186. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  187. package/dist/tools/builtins/sandbox/common.js +14 -1
  188. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  189. package/dist/tools/builtins/sandbox/index.js +4 -1
  190. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  191. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  192. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  193. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  194. package/dist/tools/builtins/web/extract.d.ts +2 -4
  195. package/dist/tools/builtins/web/extract.js +2 -5
  196. package/dist/tools/pipeline.d.ts +1 -1
  197. package/dist/tools/pipeline.js +1 -1
  198. package/dist/tools/questions.service.d.ts +15 -6
  199. package/dist/tools/questions.service.js +20 -8
  200. package/dist/tools/registry.d.ts +4 -2
  201. package/dist/tools/registry.js +4 -3
  202. package/dist/tools/tools.module.js +4 -3
  203. package/package.json +3 -3
  204. package/src/migrations/0118_device_policy_ops.sql +6 -0
  205. package/src/migrations/0119_outbox.sql +22 -0
  206. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
package/dist/overrides.js CHANGED
@@ -3,14 +3,18 @@ import { ADMIN_USAGE_TIMINGS } from "./admin/usage.service.js";
3
3
  import { CHANNEL_WEBHOOK_TIMINGS } from "./channels/webhook.js";
4
4
  import { EVAL_REPLAY_HOOKS } from "./evals/worker.js";
5
5
  import { BOOTSTRAP_LOCK_TTL } from "./extensions/bootstrap.js";
6
+ import { USAGE_SUBSCRIBERS_CLOCK } from "./extensions/usage-subscribers.js";
6
7
  import { FILE_PREVIEW_TIMEOUT } from "./files/previewers.js";
8
+ import { OUTBOX_DISPATCH_FAILS } from "./infra/outbox/outbox.dispatcher.js";
9
+ import { OUTBOX_FAST_PATH_SETTLED } from "./infra/outbox/outbox.module.js";
10
+ import { OUTBOX_STRICT_CONFLICTS } from "./infra/outbox/outbox.service.js";
7
11
  import { PRESENCE_INTERVAL_MS } from "./infra/presence/replica-presence.js";
8
12
  import { LIST_MODELS_TIMEOUT } from "./runtime/providers.js";
9
13
  import { RUN_CONTEXT_CLOCK } from "./runtime/run-context.service.js";
14
+ import { ENSURE_RUN_FAILS } from "./runtime/run-queue.js";
10
15
  import { AGENT_LOOP_FACTORY } from "./runtime/run-steps.js";
11
16
  import { RUN_HOOKS } from "./runtime/run-worker.js";
12
17
  import { SUBAGENT_MESSAGES_HOOKS, } from "./runtime/subagents/messages.js";
13
- import { USAGE_REPORT_RETRY } from "./runtime/usage-report.js";
14
18
  import { SANDBOX_LEASE_TIMINGS } from "./sandbox/leases.js";
15
19
  import { SETUP_CHECK_TIMEOUT } from "./setup/setup-checks.service.js";
16
20
  import { TOOL_PIPELINE_HOOKS } from "./tools/pipeline.js";
@@ -24,7 +28,7 @@ const SEAM_TOKENS = [
24
28
  BOOTSTRAP_LOCK_TTL,
25
29
  FILE_PREVIEW_TIMEOUT,
26
30
  LIST_MODELS_TIMEOUT,
27
- USAGE_REPORT_RETRY,
31
+ USAGE_SUBSCRIBERS_CLOCK,
28
32
  SANDBOX_LEASE_TIMINGS,
29
33
  CHANNEL_WEBHOOK_TIMINGS,
30
34
  SETUP_CHECK_TIMEOUT,
@@ -32,6 +36,10 @@ const SEAM_TOKENS = [
32
36
  ADMIN_USAGE_TIMINGS,
33
37
  EMBED_TOKEN_CLOCK,
34
38
  RUN_CONTEXT_CLOCK,
39
+ OUTBOX_STRICT_CONFLICTS,
40
+ OUTBOX_DISPATCH_FAILS,
41
+ OUTBOX_FAST_PATH_SETTLED,
42
+ ENSURE_RUN_FAILS,
35
43
  APP_INTERCEPTOR,
36
44
  ];
37
45
  const allSeamsListed = true;
@@ -95,6 +95,11 @@ export const PLATFORM_EXCLUSIONS = [
95
95
  category: "human-only",
96
96
  reason: "Confirms an import review, an approval decision by the super admin",
97
97
  },
98
+ {
99
+ route: "POST /api/v1/admin/catalog/imports/:stagingId/update",
100
+ category: "human-only",
101
+ reason: "Confirms an import review together with the platform update it applies, an approval decision by the super admin",
102
+ },
98
103
  {
99
104
  route: "GET /api/v1/admin/catalog/items/:id/icon",
100
105
  category: "transport",
@@ -78,6 +78,22 @@ function attentionRows(a) {
78
78
  ? `Check the provider's deployment config and connection on its extension page (/admin/extensions/${p.plugin}); the models page (/admin/models) shows the list.`
79
79
  : "The provider lists no chat model that is switched on: switch one on in the admin console (/admin/models) or check the provider's config.",
80
80
  })),
81
+ ...a.outbox.map((o) => ({
82
+ kind: "outbox",
83
+ id: o.kind,
84
+ name: "Background work that could not be queued",
85
+ state: "dead",
86
+ reason: `${o.count} dead outbox row(s), the oldest dead since ${o.oldestAt}`,
87
+ next: "List them with admin.outbox-list-dead, then retry each with admin.outbox-retry or drop it with admin.outbox-discard.",
88
+ })),
89
+ ...a.queue.map((q) => ({
90
+ kind: "queue",
91
+ id: q.kind,
92
+ name: "The queue lost background jobs",
93
+ state: "lost_jobs",
94
+ reason: `${q.count} queued run job(s) were missing from the queue, the last at ${q.lastAt}; they were queued again`,
95
+ next: "Redis lost data: check its persistence (AOF or snapshots) and whether it restarted or evicted keys. Nothing to do per run; the item clears a week after the last loss.",
96
+ })),
81
97
  ],
82
98
  more: a.more,
83
99
  unavailable: a.unavailable,
@@ -127,7 +143,7 @@ let AdminOperations = class AdminOperations {
127
143
  this.registry.register([
128
144
  defineOperation({
129
145
  id: "admin.overview",
130
- description: "The admin console's dashboard numbers (super admin): orgs, accounts and attentionTotal, everything waiting for a super admin (the platform inbox total plus the extensions, plugin updates and model providers admin.attention lists); partial is true when one of those lists could not be read right now (attentionTotal then leaves it out).",
146
+ description: "The admin console's dashboard numbers (super admin): orgs, accounts and attentionTotal, everything waiting for a super admin (the platform inbox total plus the extensions, plugin updates and model providers admin.attention lists, every dead outbox row and the lost-jobs item of the run queue); partial is true when one of those lists could not be read right now (attentionTotal then leaves it out).",
131
147
  input: z.object({}).strict(),
132
148
  example: {},
133
149
  authz: { superAdmin: true },
@@ -137,7 +153,7 @@ let AdminOperations = class AdminOperations {
137
153
  }),
138
154
  defineOperation({
139
155
  id: "admin.attention",
140
- description: "What needs the super admin besides the platform inbox (super admin), one compact row each (kind, id, name, state, reason, next step): extensions (reason config, load_failed, approval, dependency, conflict, converging or legacy_format), platform plugins with a newer marketplace version (reason names both versions) and model providers (reason list_failed or no_chat_model). Each kind lists at most 20 rows; more counts the rest per kind. unavailable names a kind that could not be read right now (try again later). The platform inbox itself is inbox.platform-list.",
156
+ description: "What needs the super admin besides the platform inbox (super admin), one compact row each (kind, id, name, state, reason, next step): extensions (reason config, load_failed, approval, dependency, conflict, converging or legacy_format), platform plugins with a newer marketplace version (reason names both versions) model providers (reason list_failed or no_chat_model) background work the kernel could not queue (kind outbox: how many outbox rows are dead and since when) and run jobs the queue lost (kind queue: Redis lost data; the runs were queued again). Each kind lists at most 20 rows; more counts the rest per kind. unavailable names a kind that could not be read right now (try again later). The platform inbox itself is inbox.platform-list.",
141
157
  input: z.object({}).strict(),
142
158
  example: {},
143
159
  authz: { superAdmin: true },
@@ -2,6 +2,7 @@ import { type OnModuleInit } from "@nestjs/common";
2
2
  import { type AppearanceResult, AppearanceService } from "../../catalog/appearance.service.js";
3
3
  import { CatalogService } from "../../catalog/catalog.service.js";
4
4
  import { CatalogScopeService } from "../../catalog/catalog-scope.service.js";
5
+ import { PlatformImportUpdate } from "../../catalog/platform-import-update.js";
5
6
  import { PluginImport, type PluginReview } from "../../catalog/plugin-import.js";
6
7
  import { KernelDb } from "../../infra/database/kernel-db.js";
7
8
  import { ThemesService } from "../../themes/themes.service.js";
@@ -70,10 +71,11 @@ export declare class CatalogAdminOperations implements OnModuleInit {
70
71
  private readonly catalog;
71
72
  private readonly scope;
72
73
  private readonly pluginImport;
74
+ private readonly importUpdate;
73
75
  private readonly kdb;
74
76
  private readonly appearance;
75
77
  private readonly themes;
76
- constructor(registry: PlatformOperations, catalog: CatalogService, scope: CatalogScopeService, pluginImport: PluginImport, kdb: KernelDb, appearance: AppearanceService, themes: ThemesService);
78
+ constructor(registry: PlatformOperations, catalog: CatalogService, scope: CatalogScopeService, pluginImport: PluginImport, importUpdate: PlatformImportUpdate, kdb: KernelDb, appearance: AppearanceService, themes: ThemesService);
77
79
  /** The precheck refusal of another org's private plugin (the phase-1 rule), else null. */
78
80
  private otherOrgs;
79
81
  /** Whether an org owns the item (its owner org manages it, not the super admin's agent). */
@@ -14,6 +14,7 @@ import { CatalogDisabledBody, CatalogGitImportBody, CatalogIdPathParams, Catalog
14
14
  import { AppearanceService } from "../../catalog/appearance.service.js";
15
15
  import { CatalogService } from "../../catalog/catalog.service.js";
16
16
  import { CatalogScopeService } from "../../catalog/catalog-scope.service.js";
17
+ import { PlatformImportUpdate } from "../../catalog/platform-import-update.js";
17
18
  import { PluginImport } from "../../catalog/plugin-import.js";
18
19
  import { ApiError } from "../../errors.js";
19
20
  import { KernelDb } from "../../infra/database/kernel-db.js";
@@ -125,14 +126,16 @@ let CatalogAdminOperations = class CatalogAdminOperations {
125
126
  catalog;
126
127
  scope;
127
128
  pluginImport;
129
+ importUpdate;
128
130
  kdb;
129
131
  appearance;
130
132
  themes;
131
- constructor(registry, catalog, scope, pluginImport, kdb, appearance, themes) {
133
+ constructor(registry, catalog, scope, pluginImport, importUpdate, kdb, appearance, themes) {
132
134
  this.registry = registry;
133
135
  this.catalog = catalog;
134
136
  this.scope = scope;
135
137
  this.pluginImport = pluginImport;
138
+ this.importUpdate = importUpdate;
136
139
  this.kdb = kdb;
137
140
  this.appearance = appearance;
138
141
  this.themes = themes;
@@ -313,6 +316,30 @@ let CatalogAdminOperations = class CatalogAdminOperations {
313
316
  };
314
317
  },
315
318
  }),
319
+ defineOperation({
320
+ id: "plugins.import-update-preview",
321
+ description: "Preview what a staged new version of an installed platform plugin does as the platform update (super admin): every component's change, the extension step and the org connectors it touches. Nothing is imported or changed; the person confirms the review and the update together on the web (reviewUrl).",
322
+ input: z.object({ stagingId: StagingId }).strict(),
323
+ example: { stagingId: "3f1c2a9e-5b7d-4e21-9c0a-1d2e3f4a5b6c" },
324
+ authz: { superAdmin: true },
325
+ mirrors: ["GET /api/v1/admin/catalog/imports/:stagingId/update-preview"],
326
+ confirm: "read",
327
+ errors: [
328
+ {
329
+ code: "plugin.staging_not_found",
330
+ next: "The staged import expired (24 hours) or was confirmed already: prepare it again with plugins.import-npm or plugins.import-git.",
331
+ },
332
+ {
333
+ code: "plugin.upgrade_target_invalid",
334
+ next: "The plugin is not installed on the platform yet (reason not_installed: the review installs it), or the platform cannot move to this version (the details say why).",
335
+ },
336
+ ],
337
+ run: async (input) => ({
338
+ preview: await this.importUpdate.preview(input.stagingId),
339
+ reviewUrl: reviewUrl(input.stagingId),
340
+ next: CONFIRM_ON_WEB,
341
+ }),
342
+ }),
316
343
  defineOperation({
317
344
  id: "plugins.scope-preview",
318
345
  description: "Preview changing where a platform plugin is on (super admin): a scope (all: every org; optional: orgs switch it on; named: only the orgs listed, with orgs) or disabled: true (off everywhere). Returns the orgs that lose it (with the agents there), the components it takes back, name conflicts, and the scopeHash plugins.set-scope or plugins.set-disabled needs. Nothing is written; show the preview to the person first.",
@@ -675,8 +702,9 @@ CatalogAdminOperations = __decorate([
675
702
  __param(1, Inject(CatalogService)),
676
703
  __param(2, Inject(CatalogScopeService)),
677
704
  __param(3, Inject(PluginImport)),
678
- __param(4, Inject(KernelDb)),
679
- __param(5, Inject(AppearanceService)),
680
- __param(6, Inject(ThemesService))
705
+ __param(4, Inject(PlatformImportUpdate)),
706
+ __param(5, Inject(KernelDb)),
707
+ __param(6, Inject(AppearanceService)),
708
+ __param(7, Inject(ThemesService))
681
709
  ], CatalogAdminOperations);
682
710
  export { CatalogAdminOperations };
@@ -0,0 +1,21 @@
1
+ import { type OnModuleInit } from "@nestjs/common";
2
+ import { OutboxDeadService } from "../../admin/outbox-dead.service.js";
3
+ import { KernelDb } from "../../infra/database/kernel-db.js";
4
+ import { PlatformOperations } from "../registry.js";
5
+ /**
6
+ * The super admin's dead outbox rows through the `Platform` tool (transactional outbox spec
7
+ * §3.4): the list, retry and discard of `/api/v1/admin/outbox/dead*`, run through the same
8
+ * service. Dead rows are the deployment's, not an org's: the operations act on all of them.
9
+ */
10
+ export declare class OutboxOperations implements OnModuleInit {
11
+ private readonly registry;
12
+ private readonly dead;
13
+ private readonly kdb;
14
+ constructor(registry: PlatformOperations, dead: OutboxDeadService, kdb: KernelDb);
15
+ /** The dead row's job (queue and name) for a card, or null when no dead row has the id. */
16
+ private jobOf;
17
+ private precheck;
18
+ /** "job-name on queue" for a card, or the bare id when the row is gone. */
19
+ private label;
20
+ onModuleInit(): void;
21
+ }
@@ -0,0 +1,125 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Inject, Injectable } from "@nestjs/common";
11
+ import { z } from "zod";
12
+ import { OutboxDeadParams } from "../../admin/outbox-dead.schemas.js";
13
+ import { OutboxDeadService } from "../../admin/outbox-dead.service.js";
14
+ import { KernelDb } from "../../infra/database/kernel-db.js";
15
+ import { defineOperation } from "../operation.js";
16
+ import { PlatformOperations } from "../registry.js";
17
+ import { viaAgent } from "./errors.js";
18
+ const DeadIdInput = OutboxDeadParams.strict();
19
+ /**
20
+ * The super admin's dead outbox rows through the `Platform` tool (transactional outbox spec
21
+ * §3.4): the list, retry and discard of `/api/v1/admin/outbox/dead*`, run through the same
22
+ * service. Dead rows are the deployment's, not an org's: the operations act on all of them.
23
+ */
24
+ let OutboxOperations = class OutboxOperations {
25
+ registry;
26
+ dead;
27
+ kdb;
28
+ constructor(registry, dead, kdb) {
29
+ this.registry = registry;
30
+ this.dead = dead;
31
+ this.kdb = kdb;
32
+ }
33
+ /** The dead row's job (queue and name) for a card, or null when no dead row has the id. */
34
+ async jobOf(id) {
35
+ const row = await this.kdb.db
36
+ .selectFrom("outbox")
37
+ .select(["queue", "job_name"])
38
+ .where("id", "=", id)
39
+ .where("dead_at", "is not", null)
40
+ .executeTakeFirst();
41
+ return row ?? null;
42
+ }
43
+ async precheck(id) {
44
+ return (await this.jobOf(id)) === null
45
+ ? { ok: false, code: "outbox.not_found", message: `No dead outbox row ${id}.` }
46
+ : { ok: true };
47
+ }
48
+ /** "job-name on queue" for a card, or the bare id when the row is gone. */
49
+ async label(id) {
50
+ const job = await this.jobOf(id);
51
+ return job === null ? `#${id}` : `${job.job_name} (${job.queue}, #${id})`;
52
+ }
53
+ onModuleInit() {
54
+ this.registry.register([
55
+ defineOperation({
56
+ id: "admin.outbox-list-dead",
57
+ description: "List background work the kernel could not queue (super admin): outbox rows dead after their last delivery attempt, oldest first (at most 200), each with its id, queue, job name, job id, org (null for platform work), attempts, last error and when it died. Retry one with admin.outbox-retry or drop it with admin.outbox-discard.",
58
+ input: z.object({}).strict(),
59
+ example: {},
60
+ authz: { superAdmin: true },
61
+ mirrors: ["GET /api/v1/admin/outbox/dead"],
62
+ confirm: "read",
63
+ run: () => this.dead.list(),
64
+ }),
65
+ defineOperation({
66
+ id: "admin.outbox-retry",
67
+ description: "Queue a dead outbox row again (super admin): it gets fresh attempts and is delivered right away. When the same job is already owed by a pending row, the dead copy is discarded instead; outcome says which (retried or already_owed). A job whose subject is gone since is harmless: it finds nothing to do. The platform audit log records it.",
68
+ input: DeadIdInput,
69
+ example: { id: "42" },
70
+ authz: { superAdmin: true },
71
+ mirrors: ["POST /api/v1/admin/outbox/dead/:id/retry"],
72
+ confirm: "platform",
73
+ errors: [
74
+ {
75
+ code: "outbox.not_found",
76
+ next: "The row is no longer dead (retried, discarded or delivered): list the dead rows again with admin.outbox-list-dead.",
77
+ },
78
+ ],
79
+ precheck: (i) => this.precheck(i.id),
80
+ summary: async (i) => {
81
+ const job = await this.label(i.id);
82
+ return {
83
+ zh: `重新排入无法排入队列的后台任务 ${job}`,
84
+ en: `Queue background work that could not be queued again: ${job}`,
85
+ };
86
+ },
87
+ run: (input, ctx) => this.dead.retry(input.id, { accountId: ctx.actorAccountId }, viaAgent("admin.outbox-retry", ctx)),
88
+ }),
89
+ defineOperation({
90
+ id: "admin.outbox-discard",
91
+ description: "Discard a dead outbox row (super admin): its job is never run. Use it only when the work is no longer wanted; to run it, use admin.outbox-retry. The platform audit log records it.",
92
+ input: DeadIdInput,
93
+ example: { id: "42" },
94
+ authz: { superAdmin: true },
95
+ mirrors: ["DELETE /api/v1/admin/outbox/dead/:id"],
96
+ confirm: "platform",
97
+ errors: [
98
+ {
99
+ code: "outbox.not_found",
100
+ next: "The row is no longer dead (retried, discarded or delivered): list the dead rows again with admin.outbox-list-dead.",
101
+ },
102
+ ],
103
+ precheck: (i) => this.precheck(i.id),
104
+ summary: async (i) => {
105
+ const job = await this.label(i.id);
106
+ return {
107
+ zh: `丢弃无法排入队列的后台任务 ${job},它不会再运行`,
108
+ en: `Discard background work that could not be queued: ${job}; it will never run`,
109
+ };
110
+ },
111
+ run: async (input, ctx) => {
112
+ await this.dead.discard(input.id, { accountId: ctx.actorAccountId }, viaAgent("admin.outbox-discard", ctx));
113
+ return { id: input.id, discarded: true };
114
+ },
115
+ }),
116
+ ]);
117
+ }
118
+ };
119
+ OutboxOperations = __decorate([
120
+ Injectable(),
121
+ __param(0, Inject(PlatformOperations)),
122
+ __param(1, Inject(OutboxDeadService)),
123
+ __param(2, Inject(KernelDb))
124
+ ], OutboxOperations);
125
+ export { OutboxOperations };
@@ -10,7 +10,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { Inject, Injectable } from "@nestjs/common";
11
11
  import { z } from "zod";
12
12
  import { ScheduledTasksService, } from "../../schedules/scheduled-tasks.service.js";
13
- import { createInputOf, grantsOf, ScheduleChatTargetsQuery, ScheduleCreateBody, ScheduleGrantableQuery, ScheduleGrantParams, ScheduleGrantsBody, ScheduleListQuery, ScheduleParams, ScheduleRunBody, ScheduleRunsQuery, ScheduleUpdateBody, updateInputOf, } from "../../schedules/schedules.schemas.js";
13
+ import { calendarInputOf, createInputOf, grantsOf, ScheduleCalendarQuery, ScheduleChatTargetsQuery, ScheduleCreateBody, ScheduleGrantableQuery, ScheduleGrantParams, ScheduleGrantsBody, ScheduleListQuery, ScheduleParams, ScheduleRunBody, ScheduleRunsQuery, ScheduleUpdateBody, updateInputOf, } from "../../schedules/schedules.schemas.js";
14
14
  import { defineOperation } from "../operation.js";
15
15
  import { PlatformOperations } from "../registry.js";
16
16
  import { named, orElse } from "./summary.js";
@@ -98,6 +98,45 @@ let SchedulesOperations = class SchedulesOperations {
98
98
  return { items: items.map(row) };
99
99
  },
100
100
  }),
101
+ defineOperation({
102
+ id: "schedules.calendar",
103
+ description: "Your scheduled task runs and planned occurrences between two instants (at most 43 days): recorded runs with their status, planned runs computed from each active task's schedule up to 42 days ahead, and dense tasks summarized per day. Org admins see every task with all=true. from and to are ISO instants with an offset; timeZone (IANA) sets the local days used to summarize dense tasks; taskId lists one task's items, never summarized (then at most 2 days). truncated means a limit cut the range at horizon.",
104
+ input: ScheduleCalendarQuery,
105
+ example: {
106
+ from: "2026-11-09T00:00:00+08:00",
107
+ to: "2026-11-16T00:00:00+08:00",
108
+ timeZone: "Asia/Shanghai",
109
+ },
110
+ authz: { orgAction: "org.read" },
111
+ mirrors: ["GET /api/v1/orgs/:orgId/schedules/calendar"],
112
+ confirm: "read",
113
+ errors: [
114
+ {
115
+ code: "validation_failed",
116
+ next: "Narrow the range to 43 days or less (2 days with taskId), with from before to.",
117
+ },
118
+ {
119
+ code: "forbidden",
120
+ next: "Reading every task needs the org admin role: call without all, or ask an org admin.",
121
+ },
122
+ {
123
+ code: "schedule.not_found",
124
+ next: "Check taskId with schedules.list: members see only their own tasks; an org admin finds another member's task id with schedules.list all=true, then reads it here by taskId (all is not needed).",
125
+ },
126
+ ],
127
+ run: async (input, ctx) => {
128
+ const answer = await this.tasks.calendar(ctx.orgId, this.actor(ctx), calendarInputOf(input));
129
+ return {
130
+ ...answer,
131
+ tasks: answer.tasks.map((t) => ({
132
+ id: t.id,
133
+ name: t.name,
134
+ agentId: t.agentId,
135
+ status: t.status,
136
+ })),
137
+ };
138
+ },
139
+ }),
101
140
  defineOperation({
102
141
  id: "schedules.chat-targets",
103
142
  description: "The IM chats you can point a chat-mode scheduled task at: your direct chats with each active channel connection's bot (one you have messaged before), and the groups you have talked in when the channel has threads. Each gives { connectionId, chatId, chatType } for schedules.create, and the connection's agentId, which the task must use.",
@@ -21,6 +21,7 @@ import { ModelsOperations } from "./ops/models.ops.js";
21
21
  import { OrgPluginsOperations } from "./ops/org-plugins.ops.js";
22
22
  import { OrgSettingsOperations } from "./ops/org-settings.ops.js";
23
23
  import { OrgsOperations } from "./ops/orgs.ops.js";
24
+ import { OutboxOperations } from "./ops/outbox.ops.js";
24
25
  import { PlatformOperationsProvider } from "./ops/platform.ops.js";
25
26
  import { PluginsOperations } from "./ops/plugins.ops.js";
26
27
  import { PreviewersOperations } from "./ops/previewers.ops.js";
@@ -34,7 +35,7 @@ import { UiOperations } from "./ops/ui.ops.js";
34
35
  * One provider per domain, each taking `PlatformOperations` as its first constructor argument:
35
36
  * the parity test in `tools/checks` builds the operations from it without a Nest app.
36
37
  */
37
- export declare const PLATFORM_OPERATION_PROVIDERS: readonly [typeof PlatformOperationsProvider, typeof SkillsOperations, typeof AgentsOperations, typeof AgentTemplatesOperations, typeof PluginsOperations, typeof MarketplacesOperations, typeof RequestsOperations, typeof ConnectorsOperations, typeof ExtensionsOperations, typeof ConversationsOperations, typeof ComponentsOperations, typeof AccountsOperations, typeof OrgsOperations, typeof AdminOperations, typeof CatalogAdminOperations, typeof OrgPluginsOperations, typeof ExtensionsAdminOperations, typeof ModelsOperations, typeof InboxOperations, typeof ApprovalsOperations, typeof ChannelsOperations, typeof OrgSettingsOperations, typeof EvalsOperations, typeof BackgroundOperations, typeof PreviewersOperations, typeof DevicesOperations, typeof AgentSkillsOperations, typeof ThemesOperations, typeof SetupOperations, typeof UiOperations, typeof AdminItemsOperations, typeof SchedulesOperations];
38
+ export declare const PLATFORM_OPERATION_PROVIDERS: readonly [typeof PlatformOperationsProvider, typeof SkillsOperations, typeof AgentsOperations, typeof AgentTemplatesOperations, typeof PluginsOperations, typeof MarketplacesOperations, typeof RequestsOperations, typeof ConnectorsOperations, typeof ExtensionsOperations, typeof ConversationsOperations, typeof ComponentsOperations, typeof AccountsOperations, typeof OrgsOperations, typeof AdminOperations, typeof CatalogAdminOperations, typeof OrgPluginsOperations, typeof ExtensionsAdminOperations, typeof ModelsOperations, typeof InboxOperations, typeof ApprovalsOperations, typeof ChannelsOperations, typeof OrgSettingsOperations, typeof EvalsOperations, typeof BackgroundOperations, typeof PreviewersOperations, typeof DevicesOperations, typeof AgentSkillsOperations, typeof ThemesOperations, typeof SetupOperations, typeof UiOperations, typeof AdminItemsOperations, typeof SchedulesOperations, typeof OutboxOperations];
38
39
  /**
39
40
  * The `Platform` tool's operations (ruling P2): one provider per domain, each registering its
40
41
  * operations into `PlatformOperations` from `onModuleInit`. It imports the domain modules, which
@@ -49,6 +49,7 @@ import { ModelsOperations } from "./ops/models.ops.js";
49
49
  import { OrgPluginsOperations } from "./ops/org-plugins.ops.js";
50
50
  import { OrgSettingsOperations } from "./ops/org-settings.ops.js";
51
51
  import { OrgsOperations } from "./ops/orgs.ops.js";
52
+ import { OutboxOperations } from "./ops/outbox.ops.js";
52
53
  import { PlatformOperationsProvider } from "./ops/platform.ops.js";
53
54
  import { PluginsOperations } from "./ops/plugins.ops.js";
54
55
  import { PreviewersOperations } from "./ops/previewers.ops.js";
@@ -107,6 +108,8 @@ export const PLATFORM_OPERATION_PROVIDERS = [
107
108
  AdminItemsOperations,
108
109
  // scheduled tasks (scheduled tasks spec §10)
109
110
  SchedulesOperations,
111
+ // dead outbox rows (transactional outbox spec §3.4)
112
+ OutboxOperations,
110
113
  ];
111
114
  /**
112
115
  * The `Platform` tool's operations (ruling P2): one provider per domain, each registering its
@@ -2,6 +2,7 @@ import { ADMIN_ROUTES } from "../admin/admin.schemas.js";
2
2
  import { ADMIN_ITEM_ROUTES } from "../admin/admin-items.schemas.js";
3
3
  import { AUDIT_ROUTES } from "../admin/audit.schemas.js";
4
4
  import { ADMIN_EXTENSION_ROUTES } from "../admin/extensions.schemas.js";
5
+ import { OUTBOX_DEAD_ROUTES } from "../admin/outbox-dead.schemas.js";
5
6
  import { OVERVIEW_ROUTES } from "../admin/overview.schemas.js";
6
7
  import { AGENT_ROUTES } from "../agents/agents.schemas.js";
7
8
  import { AGENT_TEMPLATE_ROUTES } from "../agents/templates.schemas.js";
@@ -85,4 +86,6 @@ export const ROUTE_SCHEMAS = [
85
86
  // the admin overview and the platform-level audit log (admin overview spec §5.2)
86
87
  ...OVERVIEW_ROUTES,
87
88
  ...AUDIT_ROUTES,
89
+ // dead outbox rows (transactional outbox spec §3.4)
90
+ ...OUTBOX_DEAD_ROUTES,
88
91
  ];
@@ -25,16 +25,18 @@ import { EvalWorkersModule } from "../evals/evals.module.js";
25
25
  import { ExtensionsModule } from "../extensions/extensions.module.js";
26
26
  import { PluginsReconcileJob } from "../extensions/lifecycle.js";
27
27
  import { InitialPasswordCleanupJob } from "../identity/initial-passwords.job.js";
28
+ import { OutboxRelayModule } from "../infra/outbox/outbox-relay.module.js";
28
29
  import { KernelScheduleModule } from "../infra/schedule/schedule.module.js";
29
30
  import { DirectoryScanJob } from "../orgs/directory-sync.js";
30
31
  import { OffboardSweepJob } from "../orgs/offboarding.js";
31
32
  import { OrgLockModule } from "../orgs/org-lock.js";
32
33
  import { OrgsModule } from "../orgs/orgs.module.js";
33
34
  import { MaintenanceModule } from "../runtime/maintenance.js";
35
+ import { RunJobsModule } from "../runtime/run-jobs.js";
36
+ import { RunOrphanCheck } from "../runtime/run-orphan-check.job.js";
34
37
  import { RuntimeWorkersModule } from "../runtime/run-processors.js";
35
38
  import { RuntimeModule } from "../runtime/runtime.module.js";
36
- import { UsageReportModule, UsageSweepJob } from "../runtime/usage-report.js";
37
- import { WaitSweepJob } from "../runtime/waits.js";
39
+ import { UsageReportModule } from "../runtime/usage-report.js";
38
40
  import { SandboxSweepJobs } from "../sandbox/jobs.js";
39
41
  import { SandboxModule } from "../sandbox/sandbox.module.js";
40
42
  import { ScheduledTaskFireModule } from "../schedules/fire.js";
@@ -58,6 +60,8 @@ let WorkerModule = WorkerModule_1 = class WorkerModule {
58
60
  module: WorkerModule_1,
59
61
  imports: [
60
62
  KernelScheduleModule,
63
+ // the outbox relay (`@LockedInterval`): delivers what the fast path did not
64
+ OutboxRelayModule,
61
65
  ClientModule,
62
66
  // the queue consumers (their worker options come from the startup config)
63
67
  RuntimeWorkersModule.forRoot(config),
@@ -67,6 +71,7 @@ let WorkerModule = WorkerModule_1 = class WorkerModule {
67
71
  PluginScopeWorkersModule,
68
72
  // the domains the fixed-period jobs call
69
73
  RuntimeModule,
74
+ RunJobsModule,
70
75
  UsageReportModule,
71
76
  TitlesModule,
72
77
  AttachmentsModule,
@@ -84,8 +89,8 @@ let WorkerModule = WorkerModule_1 = class WorkerModule {
84
89
  ScheduledTaskFireModule,
85
90
  ],
86
91
  providers: [
87
- WaitSweepJob,
88
- UsageSweepJob,
92
+ // the one repair of a run nobody runs (transactional outbox spec §8.3)
93
+ RunOrphanCheck,
89
94
  AttachmentCleanupJob,
90
95
  StoragePurgeJob,
91
96
  SandboxSweepJobs,
@@ -94,7 +94,8 @@ export class LangGraphLoop {
94
94
  const config = {
95
95
  configurable: { thread_id: ctx.runId },
96
96
  // 每步最多 3 个超步(prepare、model、tools),再加 wrapUp 与余量。
97
- recursionLimit: ctx.limits.maxSteps * 3 + 10,
97
+ // no step limit: no recursion limit either (the loop ends when the model stops calling tools)
98
+ recursionLimit: ctx.limits.maxSteps === undefined ? Number.MAX_SAFE_INTEGER : ctx.limits.maxSteps * 3 + 10,
98
99
  durability: "sync",
99
100
  };
100
101
  const existing = await this.saver.getTuple({ configurable: { thread_id: ctx.runId } });
@@ -125,7 +126,12 @@ export class LangGraphLoop {
125
126
  : { kind: outcome, consumedUpToSeq: final.consumedUpToSeq };
126
127
  }
127
128
  build(ctx) {
128
- const timedOut = () => Date.now() >= ctx.limits.deadline;
129
+ // an absent limit is no limit (`runtime.maxSteps` / `runtime.maxRunSeconds` 0)
130
+ const { maxSteps } = ctx.limits;
131
+ const timedOut = () => {
132
+ const deadline = ctx.limits.deadline;
133
+ return deadline !== undefined && Date.now() >= deadline;
134
+ };
129
135
  // 本次运行的附件字节缓存:各步共用,运行结束即丢弃(只是优化,见 MediaCache)
130
136
  const mediaCache = new MediaCache(this.stepDeps.config.get().attachments.requestMediaBytes);
131
137
  const input = (step, absorbedUpToSeq) => ({
@@ -138,7 +144,7 @@ export class LangGraphLoop {
138
144
  signal: ctx.signal,
139
145
  sink: ctx.sink,
140
146
  absorbedUpToSeq,
141
- maxSteps: ctx.limits.maxSteps,
147
+ ...(maxSteps !== undefined && { maxSteps }),
142
148
  mediaCache,
143
149
  agentId: ctx.agentId,
144
150
  accountId: ctx.triggerAccountId,
@@ -216,7 +222,7 @@ export class LangGraphLoop {
216
222
  agent: ctx.agent,
217
223
  accountId: ctx.triggerAccountId,
218
224
  signal: ctx.signal,
219
- deadline: ctx.limits.deadline,
225
+ ...(ctx.limits.deadline !== undefined && { deadline: ctx.limits.deadline }),
220
226
  ...(ctx.conversationKind !== undefined && { conversationKind: ctx.conversationKind }),
221
227
  ...(ctx.parent !== undefined && { parent: ctx.parent }),
222
228
  });
@@ -256,7 +262,9 @@ export class LangGraphLoop {
256
262
  // (aborted / completed: a fork invocation's run ended in its first prepare, phase D)
257
263
  if (s.outcome !== null)
258
264
  return END;
259
- return s.budgetWrapUp === true || s.step > ctx.limits.maxSteps ? "wrapUp" : "model";
265
+ return s.budgetWrapUp === true || (maxSteps !== undefined && s.step > maxSteps)
266
+ ? "wrapUp"
267
+ : "model";
260
268
  }, ["model", "wrapUp", END])
261
269
  .addConditionalEdges("model", (s) => (s.budgetWrapUp === true ? "wrapUp" : s.outcome === null ? "tools" : END), ["tools", "wrapUp", END])
262
270
  .addEdge("tools", "prepare")
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Run limits where 0 means "no limit" (`runtime.maxSteps`, `runtime.maxRunSeconds`, a sub-agent's
3
+ * logged `maxSteps`): like Claude Code, a run without limits goes on until the model stops calling
4
+ * tools or someone stops it.
5
+ */
6
+ /**
7
+ * The tightest of the given limits, ignoring absent and non-positive ones (0 = no limit);
8
+ * `undefined` when none of them limits anything.
9
+ */
10
+ export declare function tightestLimit(...limits: (number | undefined)[]): number | undefined;
11
+ /**
12
+ * The run's deadline (epoch ms) for `runtime.maxRunSeconds` counted from `startMs`; `undefined`
13
+ * when the run has no duration limit (0).
14
+ */
15
+ export declare function runDeadline(startMs: number, maxRunSeconds: number): number | undefined;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Run limits where 0 means "no limit" (`runtime.maxSteps`, `runtime.maxRunSeconds`, a sub-agent's
3
+ * logged `maxSteps`): like Claude Code, a run without limits goes on until the model stops calling
4
+ * tools or someone stops it.
5
+ */
6
+ /**
7
+ * The tightest of the given limits, ignoring absent and non-positive ones (0 = no limit);
8
+ * `undefined` when none of them limits anything.
9
+ */
10
+ export function tightestLimit(...limits) {
11
+ let out;
12
+ for (const l of limits) {
13
+ if (l === undefined || !(l > 0))
14
+ continue;
15
+ out = out === undefined ? l : Math.min(out, l);
16
+ }
17
+ return out;
18
+ }
19
+ /**
20
+ * The run's deadline (epoch ms) for `runtime.maxRunSeconds` counted from `startMs`; `undefined`
21
+ * when the run has no duration limit (0).
22
+ */
23
+ export function runDeadline(startMs, maxRunSeconds) {
24
+ return maxRunSeconds > 0 ? startMs + maxRunSeconds * 1000 : undefined;
25
+ }
@@ -27,9 +27,13 @@ export interface LoopContext {
27
27
  * 即 seq 也大于会话的 `consumed_seq`),按 seq 升序。loop 记录吸收到的最大 seq 作为 `consumedUpToSeq`。
28
28
  */
29
29
  pendingAbsorbable(afterSeq: number): Promise<AbsorbableEvent[]>;
30
+ /**
31
+ * The run's limits; an absent one is no limit (`runtime.maxSteps` / `runtime.maxRunSeconds` 0):
32
+ * `maxSteps` model steps before the final wrap-up, `deadline` in epoch ms.
33
+ */
30
34
  limits: {
31
- maxSteps: number;
32
- deadline: number;
35
+ maxSteps?: number;
36
+ deadline?: number;
33
37
  };
34
38
  /**
35
39
  * Kind of the conversation: plugin hooks fire for `chat` and `subagent` (F10, P-18, sub-agents
@@ -124,6 +128,7 @@ export interface AgentLoop {
124
128
  /**
125
129
  * The wait of a step whose sub-agents still run (spec §7.2): id `sa-<runId>-<step>`, expiring after
126
130
  * the approval timeout plus one run's duration plus 10 minutes (a child may itself wait for an
127
- * approval); the timeout is a safety net.
131
+ * approval); the timeout is a safety net. Without a run duration limit (`runtime.maxRunSeconds` 0)
132
+ * it is the approval timeout plus 10 minutes.
128
133
  */
129
134
  export declare function subagentsWait(runId: string, step: number, config: Pick<KernelConfig, "tools" | "runtime">, now?: number): WaitingFor;
@@ -8,7 +8,8 @@ export const subagentsWaitId = (runId, step) => `sa-${runId}-${step}`;
8
8
  /**
9
9
  * The wait of a step whose sub-agents still run (spec §7.2): id `sa-<runId>-<step>`, expiring after
10
10
  * the approval timeout plus one run's duration plus 10 minutes (a child may itself wait for an
11
- * approval); the timeout is a safety net.
11
+ * approval); the timeout is a safety net. Without a run duration limit (`runtime.maxRunSeconds` 0)
12
+ * it is the approval timeout plus 10 minutes.
12
13
  */
13
14
  export function subagentsWait(runId, step, config, now = Date.now()) {
14
15
  const ms = config.tools.approvalTimeoutHours * 3_600_000 + config.runtime.maxRunSeconds * 1000 + 600_000;