@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
@@ -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;
@@ -16,7 +16,9 @@ export declare const MAINTENANCE_CONCURRENCY = 5;
16
16
  * The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
17
17
  * each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
18
18
  * `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
19
- * {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `owned-plugin-cleanup` →
19
+ * {@link DirectorySync}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
20
+ * {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
21
+ * runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
20
22
  * {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
21
23
  * such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
22
24
  * maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
@@ -34,7 +36,7 @@ export declare class MaintenanceProcessor extends WorkerHost<Worker> {
34
36
  private readonly scheduled;
35
37
  private readonly log;
36
38
  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">);
39
+ constructor(waits: Pick<Waits, "processTimeout" | "childFinished" | "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, 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">);
38
40
  /**
39
41
  * Removes the retired job schedulers (`RETIRED_SCHEDULER_IDS`, runtime/retired-schedulers.ts): every fixed-period
40
42
  * maintenance job is a `@LockedCron` / `@LockedInterval` now. Idempotent; called once when a
@@ -31,8 +31,9 @@ import { DRIFT_CONNECTOR_JOB } from "../tools/job-ids.js";
31
31
  import { ToolsModule } from "../tools/tools.module.js";
32
32
  import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
33
33
  import { RETIRED_SCHEDULER_IDS } from "./retired-schedulers.js";
34
- import { RunQueue } from "./run-queue.js";
34
+ import { ENSURE_RUN_JOB, RunQueue } from "./run-queue.js";
35
35
  import { RuntimeModule } from "./runtime.module.js";
36
+ import { DELIVER_TO_PARENT_JOB, processDeliverToParent, processStopTree, STOP_TREE_JOB, } from "./subagents/tree-jobs.js";
36
37
  import { WAIT_TIMEOUT_JOB, Waits } from "./waits.js";
37
38
  export { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
38
39
  /** The maintenance worker's concurrency (as the hand-made worker had). */
@@ -41,7 +42,9 @@ export const MAINTENANCE_CONCURRENCY = 5;
41
42
  * The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
42
43
  * each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
43
44
  * `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
44
- * {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `owned-plugin-cleanup` →
45
+ * {@link DirectorySync}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
46
+ * {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
47
+ * runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
45
48
  * {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
46
49
  * such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
47
50
  * maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
@@ -113,6 +116,18 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
113
116
  await this.runs.ensureRun(conversationId, { wakeRetry: (retry ?? 0) + 1 });
114
117
  return;
115
118
  }
119
+ // the ensure-run backstop of a message, wake or run end (transactional outbox spec §6)
120
+ case ENSURE_RUN_JOB:
121
+ await this.runs.ensureRun(job.data.conversationId);
122
+ return;
123
+ // the stops a run's end or a root's delete owes its sub-agent tree (outbox spec §6)
124
+ case STOP_TREE_JOB:
125
+ await processStopTree(this.waits, job.data);
126
+ return;
127
+ // a finished sub-agent run's delivery to its parent (transactional outbox spec §6)
128
+ case DELIVER_TO_PARENT_JOB:
129
+ await processDeliverToParent(this.waits, job.data);
130
+ return;
116
131
  // a removed org-owned plugin's extension (plugin scope phase 3, P3-20)
117
132
  case OWNED_PLUGIN_CLEANUP_JOB:
118
133
  await this.ownedCleanup.run(job.data.itemId);
@@ -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 {
@@ -121,7 +118,8 @@ export interface ModelStepInput {
121
118
  parentRunId?: string;
122
119
  /**
123
120
  * 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`.
121
+ * may be below `runtime.maxSteps` (final review m4). Absent: the run has no step limit, and no
122
+ * reminder is sent.
125
123
  */
126
124
  maxSteps?: number;
127
125
  }
@@ -176,15 +174,15 @@ export declare function runModelStep(deps: ModelStepDeps, input: ModelStepInput)
176
174
  export declare function runFinalWrapUp(deps: ModelStepDeps, input: ModelStepInput, reason?: WrapUpReason): Promise<ModelStepResult>;
177
175
  /**
178
176
  * 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.
177
+ * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
178
+ * owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
179
+ * §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
180
+ * budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
181
+ * idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
184
182
  */
185
183
  export declare function appendCounted(deps: {
186
- eventLog: Pick<EventLog, "append" | "notify">;
187
- stepLog: Pick<StepLog, "appendCounted">;
184
+ eventLog: Pick<EventLog, "notify">;
185
+ stepLog: Pick<StepLog, "appendCounted" | "appendReported">;
188
186
  }, input: {
189
187
  conversationId: string;
190
188
  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;
@@ -230,8 +224,9 @@ wrapUpReason) {
230
224
  }
231
225
  }
232
226
  const { outcome, ref } = await ladder(deps, input, events, runtime, tools, wrapUpReason, streamed);
233
- // assistant_message 与 usage 同一次 append = 同一事务。
234
- const appended = await appendCounted(deps, input, [
227
+ // assistant_message and usage in one append = one transaction, which also owes the usage's
228
+ // usage.report delivery (transactional outbox spec §8.1)
229
+ await appendCounted(deps, input, [
235
230
  {
236
231
  type: "assistant_message",
237
232
  runId,
@@ -245,10 +240,6 @@ wrapUpReason) {
245
240
  payload: { step, provider: ref.provider, model: ref.model, ...outcome.usage },
246
241
  },
247
242
  ]);
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
243
  sink({ kind: "step_end", runId, step });
253
244
  return {
254
245
  content: outcome.content,
@@ -277,18 +268,18 @@ wrapUpReason) {
277
268
  }
278
269
  /**
279
270
  * 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.
271
+ * of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
272
+ * owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
273
+ * §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
274
+ * budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
275
+ * idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
285
276
  */
286
277
  export async function appendCounted(deps, input, events) {
287
278
  const { conversationId, parentRunId } = input;
288
- if (parentRunId === undefined)
289
- return deps.eventLog.append(conversationId, events);
290
279
  const budgetRunId = input.budgetRunId ?? input.parent?.budgetRunId;
291
- const out = await deps.stepLog.appendCounted(conversationId, { parentRunId, ...(budgetRunId !== undefined && { budgetRunId }) }, events);
280
+ const out = parentRunId === undefined
281
+ ? await deps.stepLog.appendReported(conversationId, events)
282
+ : await deps.stepLog.appendCounted(conversationId, { parentRunId, ...(budgetRunId !== undefined && { budgetRunId }) }, events);
292
283
  if (out.toSeq !== null)
293
284
  await deps.eventLog.notify(conversationId, out.toSeq);
294
285
  return out.written;
@@ -691,9 +682,6 @@ async function maybeCompact(deps, input, events, runtime, tools) {
691
682
  });
692
683
  }
693
684
  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
685
  return mergeLocal(events, written);
698
686
  }
699
687
  /** Merges written events into the local list (idempotent hits already in it are not added twice). */
@@ -719,7 +707,9 @@ async function ladder(deps, input, events, runtime, tools, wrapUpReason, streame
719
707
  ? BUDGET_WRAP_UP_REMINDER
720
708
  : wrapUp
721
709
  ? WRAP_UP_REMINDER
722
- : turnLimitReminder(step, input.maxSteps ?? runtime.maxSteps);
710
+ : input.maxSteps === undefined
711
+ ? null
712
+ : turnLimitReminder(step, input.maxSteps);
723
713
  let evs = events;
724
714
  const rungs = [
725
715
  { ref: agent.model, fallback: false },
@@ -0,0 +1,24 @@
1
+ import type { Redis } from "ioredis";
2
+ /** The Redis hash of the lost-jobs canary: `count` and `lastAt` (ISO). */
3
+ export declare const QUEUE_LOST_JOBS_KEY = "capk:queue-lost-jobs";
4
+ /** How long the canary stays after its last report (then the overview drops it). */
5
+ export declare const QUEUE_LOST_JOBS_TTL_SECONDS: number;
6
+ /** What the admin overview shows: how many run jobs the queue lost, and when it last did. */
7
+ export interface QueueLostJobsSummary {
8
+ count: number;
9
+ lastAt: string;
10
+ }
11
+ /**
12
+ * The "queue lost jobs" canary (transactional outbox spec §8.3): the `run-orphan-check` found
13
+ * queued runs whose job is neither in BullMQ nor owed by the outbox, which the outbox makes
14
+ * impossible unless Redis lost data (its durability broke). Each report adds to a count in Redis
15
+ * that the admin overview shows as an attention item until a week after the last report.
16
+ */
17
+ export declare class QueueLostJobs {
18
+ private readonly redis;
19
+ constructor(redis: Redis);
20
+ /** Adds `n` lost jobs to the canary (no-op for 0). */
21
+ report(n: number): Promise<void>;
22
+ /** The canary, or null when no job was lost in the last week. */
23
+ summary(): Promise<QueueLostJobsSummary | null>;
24
+ }
@@ -0,0 +1,51 @@
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 { REDIS } from "../infra/tokens.js";
12
+ /** The Redis hash of the lost-jobs canary: `count` and `lastAt` (ISO). */
13
+ export const QUEUE_LOST_JOBS_KEY = "capk:queue-lost-jobs";
14
+ /** How long the canary stays after its last report (then the overview drops it). */
15
+ export const QUEUE_LOST_JOBS_TTL_SECONDS = 7 * 24 * 3600;
16
+ /**
17
+ * The "queue lost jobs" canary (transactional outbox spec §8.3): the `run-orphan-check` found
18
+ * queued runs whose job is neither in BullMQ nor owed by the outbox, which the outbox makes
19
+ * impossible unless Redis lost data (its durability broke). Each report adds to a count in Redis
20
+ * that the admin overview shows as an attention item until a week after the last report.
21
+ */
22
+ let QueueLostJobs = class QueueLostJobs {
23
+ redis;
24
+ constructor(redis) {
25
+ this.redis = redis;
26
+ }
27
+ /** Adds `n` lost jobs to the canary (no-op for 0). */
28
+ async report(n) {
29
+ if (n <= 0)
30
+ return;
31
+ await this.redis
32
+ .multi()
33
+ .hincrby(QUEUE_LOST_JOBS_KEY, "count", n)
34
+ .hset(QUEUE_LOST_JOBS_KEY, "lastAt", new Date().toISOString())
35
+ .expire(QUEUE_LOST_JOBS_KEY, QUEUE_LOST_JOBS_TTL_SECONDS)
36
+ .exec();
37
+ }
38
+ /** The canary, or null when no job was lost in the last week. */
39
+ async summary() {
40
+ const h = await this.redis.hgetall(QUEUE_LOST_JOBS_KEY);
41
+ const count = Number(h.count ?? 0);
42
+ if (!(count > 0) || h.lastAt === undefined)
43
+ return null;
44
+ return { count, lastAt: h.lastAt };
45
+ }
46
+ };
47
+ QueueLostJobs = __decorate([
48
+ Injectable(),
49
+ __param(0, Inject(REDIS))
50
+ ], QueueLostJobs);
51
+ export { QueueLostJobs };
@@ -1,6 +1,7 @@
1
- import { KernelEvents } from "../infra/events/kernel-events.js";
1
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
2
2
  import type { KernelLogger } from "../log.js";
3
3
  import { RunAbort } from "./abort.js";
4
+ import { type ResumeOutcome, RunResume } from "./run-jobs.js";
4
5
  import { RunQueue } from "./run-queue.js";
5
6
  import { Waits } from "./waits.js";
6
7
  /**
@@ -11,13 +12,49 @@ export declare class RunControl {
11
12
  private readonly queue;
12
13
  private readonly waits;
13
14
  private readonly abort;
14
- private readonly events;
15
+ /** The resume of a waiting run in a decision's transaction (a reply's skips). */
16
+ private readonly resumes;
17
+ /** Owes a deleted root's tree stop in the delete's transaction. */
18
+ private readonly outbox;
15
19
  private readonly log;
16
- constructor(queue: Pick<RunQueue, "ensureRun" | "abortIfOrphaned" | "wakeHeldTree">, waits: Pick<Waits, "abortWaiting" | "childFinished" | "stopChildren" | "stopTree">, abort: Pick<RunAbort, "request">, events: KernelEvents, log: Pick<KernelLogger, "error">);
20
+ constructor(queue: Pick<RunQueue, "ensureRun" | "oweEnsureRun" | "abortIfOrphaned" | "wakeHeldTree" | "oweHeldTreeIn">, waits: Pick<Waits, "abortWaiting" | "childFinished" | "stopChildren" | "stopTree">, abort: Pick<RunAbort, "request">,
21
+ /** The resume of a waiting run in a decision's transaction (a reply's skips). */
22
+ resumes: Pick<RunResume, "resumeIn">,
23
+ /** Owes a deleted root's tree stop in the delete's transaction. */
24
+ outbox: Pick<OutboxService, "add">, log: Pick<KernelLogger, "error">);
17
25
  /** 有未消费的消息且没有进行中的运行时投递一轮运行;返回 runId 或 null。 */
18
26
  ensureRun(conversationId: string): Promise<string | null>;
27
+ /**
28
+ * The direct `ensureRun` right after a commit that owes the ensure-run backstop (a message, a
29
+ * queue change): a failure (Redis unreachable) is logged and never thrown, since the committed
30
+ * write is not undone and the backstop starts the run later (transactional outbox spec §6).
31
+ */
32
+ ensureRunAfterCommit(conversationId: string): Promise<void>;
33
+ /**
34
+ * Owes the ensure-run backstop in the caller's transaction (transactional outbox spec §6): `seq`
35
+ * is the event it wrote (or the conversation's newest), `orgId` the conversation's.
36
+ */
37
+ oweEnsureRun(conversationId: string, seq: number | string, orgId: string): Promise<boolean>;
19
38
  /** After a root's queue was unpaused: the wakes it held for its tree ({@link RunQueue.wakeHeldTree}). */
20
39
  wakeHeldTree(rootId: string): Promise<void>;
40
+ /**
41
+ * Owes, in the transaction that may release a root's hold (an unpause, a "send now"), the
42
+ * ensure-run backstop of every sub-agent the hold kept waiting ({@link RunQueue.oweHeldTreeIn});
43
+ * the caller's {@link wakeHeldTree} after the commit is the direct call.
44
+ */
45
+ oweHeldTreeIn(rootId: string): Promise<void>;
46
+ /**
47
+ * In the caller's transaction, after it locked the decided rows (lock order: conversations,
48
+ * decided rows, runs): resumes the waiting run `runId` when it waits on nothing else
49
+ * ({@link RunResume.resumeIn}).
50
+ */
51
+ resumeIn(runId: string, waitId: string): Promise<ResumeOutcome>;
52
+ /**
53
+ * Owes, in a root's delete transaction, the stop of its whole sub-agent tree (`stop-tree`,
54
+ * transactional outbox spec §6); the caller's {@link stopTree} after the commit is the direct
55
+ * call.
56
+ */
57
+ oweStopDeletedTree(conversationId: string, byAccountId: string | null, orgId: string): Promise<boolean>;
21
58
  /**
22
59
  * 运行是孤儿(会话锁空闲、任务已不存在,没有 worker 会收到中止广播)时直接把它写成 aborted,返回 true;
23
60
  * 否则返回 false。A stopped sub-agent then reports to its parent at once, and a stopped parent
@@ -26,8 +63,8 @@ export declare class RunControl {
26
63
  abortIfOrphaned(conversationId: string, runId: string, byAccountId: string | null): Promise<boolean>;
27
64
  /**
28
65
  * 运行在等人处理(waiting)时直接中止:取消等待、写 `run_aborted`、删除 checkpoint,返回 true;
29
- * 运行已不是 waiting(例如刚被续跑)→ false. Either way its approval / question cards may have
30
- * changed: `run.wait-stopped` lets the channel outbound update them.
66
+ * 运行已不是 waiting(例如刚被续跑)→ false. The stop's transaction owes the IM delivery that
67
+ * updates its approval / question cards (its appends, transactional outbox spec §8.1).
31
68
  */
32
69
  abortWaiting(conversationId: string, runId: string, byAccountId: string | null): Promise<boolean>;
33
70
  /**