@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
@@ -22,8 +22,6 @@ function isValidMasterKey(value) {
22
22
  * 插件名同清单规则(不能是 `local`),租户 id 非空、不含空白;与 identity/external.ts 的身份源键规则一致。
23
23
  */
24
24
  const TRUSTED_SOURCE_RE = /^(?!local:)[a-z0-9][a-z0-9._-]{0,213}:\S{1,256}$/;
25
- /** `runtime.maxRunSeconds` 的默认值。 */
26
- export const DEFAULT_MAX_RUN_SECONDS = 900;
27
25
  /** `sandbox.limits.maxCommandSeconds` 的默认值。 */
28
26
  export const DEFAULT_MAX_COMMAND_SECONDS = 7200;
29
27
  /** access token 寿命上限(秒);会话吊销标记的 TTL 按它计算,与实例当前配置无关。 */
@@ -573,9 +571,11 @@ export const KernelConfigSchema = z.object({
573
571
  maxSteps: z
574
572
  .number()
575
573
  .int()
576
- .positive()
577
- .default(50)
578
- .meta(meta({ description: "Maximum model steps per agent run before the final wrap-up" })),
574
+ .nonnegative()
575
+ .default(0)
576
+ .meta(meta({
577
+ description: "Maximum model steps per agent run before the final wrap-up; 0 (the default) means no limit: a run goes on until the model stops calling tools or someone stops it",
578
+ })),
579
579
  wakeOnBackgroundTask: z
580
580
  .boolean()
581
581
  .default(true)
@@ -585,10 +585,10 @@ export const KernelConfigSchema = z.object({
585
585
  maxRunSeconds: z
586
586
  .number()
587
587
  .int()
588
- .positive()
589
- .default(DEFAULT_MAX_RUN_SECONDS)
588
+ .nonnegative()
589
+ .default(0)
590
590
  .meta(meta({
591
- description: "Maximum wall-clock duration of one agent run in seconds (time spent waiting for people is not counted). Sandbox commands are limited to the time left in the run, so agents that need long commands need maxRunSeconds >= sandbox.limits.maxCommandSeconds",
591
+ description: "Maximum wall-clock duration of one agent run in seconds (time spent waiting for people is not counted); 0 (the default) means no limit. With a limit, sandbox commands are limited to the time left in the run, so agents that need long commands need maxRunSeconds >= sandbox.limits.maxCommandSeconds; without one, commands are limited only by sandbox.limits.maxCommandSeconds and the tool timeouts",
592
592
  })),
593
593
  streamIdleMs: z
594
594
  .number()
@@ -669,9 +669,9 @@ export const KernelConfigSchema = z.object({
669
669
  })),
670
670
  })
671
671
  .default({
672
- maxSteps: 50,
672
+ maxSteps: 0,
673
673
  wakeOnBackgroundTask: true,
674
- maxRunSeconds: DEFAULT_MAX_RUN_SECONDS,
674
+ maxRunSeconds: 0,
675
675
  streamIdleMs: 60000,
676
676
  maxOutputTokens: 16000,
677
677
  compactionThreshold: 0.8,
@@ -1178,7 +1178,7 @@ export const KernelConfigSchema = z.object({
1178
1178
  .positive()
1179
1179
  .default(DEFAULT_MAX_COMMAND_SECONDS)
1180
1180
  .meta(meta({
1181
- description: "Longest a single sandbox command may run, in seconds. A command also stops when its run is about to reach runtime.maxRunSeconds, so long commands need runtime.maxRunSeconds >= maxCommandSeconds (a startup warning is logged otherwise)",
1181
+ description: "Longest a single sandbox command may run, in seconds. When runtime.maxRunSeconds is set (not 0), a command also stops when its run is about to reach it, so long commands need runtime.maxRunSeconds >= maxCommandSeconds (a startup warning is logged otherwise)",
1182
1182
  })),
1183
1183
  suspendAfterIdleMinutes: z
1184
1184
  .number()
@@ -1246,27 +1246,29 @@ export const KernelConfigSchema = z.object({
1246
1246
  .object({
1247
1247
  max: z
1248
1248
  .enum(["strict", "dev", "open"])
1249
- .default("dev")
1249
+ .optional()
1250
1250
  .meta(meta({
1251
1251
  hot: true,
1252
- description: "Highest sandbox tier any organization may reach: strict (the allowlist only), dev (adds devHosts and local development affordances) or open (any public host); organizations and devices choose at most this",
1252
+ description: "Highest sandbox tier any organization may reach: strict (the allowlist only), dev (adds devHosts and local development affordances) or open (any public host); organizations and devices choose at most this. Unset: the super admin's choice in the admin console, else dev; set here, it is read-only there",
1253
1253
  })),
1254
1254
  default: z
1255
1255
  .enum(["strict", "dev", "open"])
1256
- .default("dev")
1256
+ .optional()
1257
1257
  .meta(meta({
1258
1258
  hot: true,
1259
- description: "Sandbox tier of a device whose owner chose none, unless its organization sets its own default; at most sandbox.tiers.max",
1259
+ description: "Sandbox tier of a device whose owner chose none, unless its organization sets its own default; at most sandbox.tiers.max. Unset: the super admin's choice in the admin console, else dev; set here, it is read-only there",
1260
1260
  })),
1261
1261
  allowUnsandboxed: z
1262
1262
  .boolean()
1263
- .default(true)
1263
+ .optional()
1264
1264
  .meta(meta({
1265
1265
  hot: true,
1266
- description: "Whether a local device may run commands without a sandbox (its owner's local choice); it only applies where the organization's ceiling is open, and an organization may forbid it",
1266
+ description: "Whether a local device may run commands without a sandbox (its owner's local choice); it only applies where the organization's ceiling is open, and an organization may forbid it. Unset: the super admin's choice in the admin console, else true; set here, it is read-only there",
1267
1267
  })),
1268
1268
  })
1269
- .refine((t) => tierAtMost(t.default, t.max), {
1269
+ // both set here: the default may not be above the max (a max from the admin console
1270
+ // caps a default set here instead, see sandbox/platform-tiers.ts)
1271
+ .refine((t) => t.default === undefined || t.max === undefined || tierAtMost(t.default, t.max), {
1270
1272
  message: "sandbox.tiers.default must not be above sandbox.tiers.max",
1271
1273
  path: ["default"],
1272
1274
  })
@@ -1441,15 +1443,6 @@ export const KernelConfigSchema = z.object({
1441
1443
  .meta(meta({
1442
1444
  description: "usage.report delivery jobs one worker replica processes at the same time",
1443
1445
  })),
1444
- usageReportKeepHours: z
1445
- .number()
1446
- .int()
1447
- .min(1)
1448
- .max(720)
1449
- .default(24)
1450
- .meta(meta({
1451
- description: "Hours a finished usage.report job is kept in Redis so re-enqueueing the same usage event (resumed step, sweeper) is deduplicated; at least 1, well above the sweeper's 10-minute overlap",
1452
- })),
1453
1446
  })
1454
1447
  .prefault({}),
1455
1448
  log: z
@@ -44,3 +44,19 @@ export interface AgentTreeNode {
44
44
  * check of the root.
45
45
  */
46
46
  export declare function readAgentTree(db: KernelSchemaDb, orgId: string, rootId: string): Promise<AgentTreeNode | null>;
47
+ /** One sub-agent a conversation dispatched, as `TaskList` lists it. */
48
+ export interface ChildAgent {
49
+ /** The child conversation's id: the agentId the parent saw. */
50
+ id: string;
51
+ /** The dispatched description (the child's title when no dispatch event has one). */
52
+ description: string;
53
+ /** The latest run's status; `queued` before the first run is recorded. */
54
+ status: "queued" | "running" | "waiting" | "completed" | "failed" | "aborted";
55
+ /** When the latest run ended; null while it is active. */
56
+ finishedAt: Date | null;
57
+ }
58
+ /**
59
+ * The sub-agents `parentId` of org `orgId` dispatched itself (not their own sub-agents), oldest
60
+ * first: live sub-agent conversations with a dispatch row, as {@link readAgentTree} counts them.
61
+ */
62
+ export declare function readChildAgents(db: KernelSchemaDb, orgId: string, parentId: string): Promise<ChildAgent[]>;
@@ -90,3 +90,39 @@ function toNode(r, isRoot) {
90
90
  };
91
91
  }
92
92
  const modelOf = (m) => m ? { provider: m.provider, model: m.model } : null;
93
+ /**
94
+ * The sub-agents `parentId` of org `orgId` dispatched itself (not their own sub-agents), oldest
95
+ * first: live sub-agent conversations with a dispatch row, as {@link readAgentTree} counts them.
96
+ */
97
+ export async function readChildAgents(db, orgId, parentId) {
98
+ const { rows } = await sql `
99
+ SELECT c.id, c.title,
100
+ (SELECT e.payload->>'description' FROM capybara.conversation_events e
101
+ WHERE e.conversation_id = c.parent_conversation_id AND e.type = 'subagent_dispatched'
102
+ AND e.payload->>'subagentId' = c.id
103
+ ORDER BY e.seq DESC LIMIT 1) AS description,
104
+ latest.status AS last_status, latest.finished_at AS last_finished_at
105
+ FROM capybara.conversations c
106
+ LEFT JOIN LATERAL (
107
+ SELECT r.status, r.finished_at FROM capybara.runs r
108
+ WHERE r.conversation_id = c.id
109
+ ORDER BY r.created_at DESC, r.id DESC
110
+ LIMIT 1
111
+ ) latest ON true
112
+ WHERE c.parent_conversation_id = ${parentId} AND c.org_id = ${orgId}
113
+ AND c.kind = 'subagent' AND c.deleted_at IS NULL
114
+ AND EXISTS (
115
+ SELECT 1 FROM capybara.subagent_dispatches sd WHERE sd.child_conversation_id = c.id
116
+ )
117
+ ORDER BY c.created_at, c.id
118
+ `.execute(db);
119
+ return rows.map((r) => {
120
+ const status = r.last_status ?? "queued";
121
+ return {
122
+ id: r.id,
123
+ description: r.description ?? r.title ?? "",
124
+ status,
125
+ finishedAt: ACTIVE.has(status) ? null : (r.last_finished_at ?? null),
126
+ };
127
+ });
128
+ }
@@ -7,7 +7,6 @@ import { ConversationDevices, type NewConversationDevice } from "../devices/conv
7
7
  import { KernelDb } from "../infra/database/kernel-db.js";
8
8
  import { TransactionHost } from "../infra/database/transaction-host.js";
9
9
  import { KernelEvents } from "../infra/events/kernel-events.js";
10
- import type { KernelLogger } from "../log.js";
11
10
  import { ProviderRegistry } from "../runtime/providers.js";
12
11
  import type { ScheduleMarker } from "../schedules/types.js";
13
12
  import { type RenderedInvocation, SkillInvoker } from "../skills/invoke.js";
@@ -118,10 +117,30 @@ export interface ReplySkips {
118
117
  export declare const NO_REPLY_SKIPS: ReplySkips;
119
118
  /** 会话服务用到的运行控制:投递运行(RunQueue)与跨副本中止(RunAbort)。 */
120
119
  export interface RunControl {
121
- /** 有未消费的消息且没有进行中的运行时投递一轮运行;返回 runId 或 null。 */
122
- ensureRun(conversationId: string): Promise<string | null>;
120
+ /**
121
+ * The direct `ensureRun` after a commit that owes the ensure-run backstop: a failure is logged,
122
+ * never thrown (transactional outbox spec §6).
123
+ */
124
+ ensureRunAfterCommit(conversationId: string): Promise<void>;
125
+ /**
126
+ * Owes the ensure-run backstop in the caller's transaction (transactional outbox spec §6): `seq`
127
+ * is the event it wrote (or the conversation's newest), `orgId` the conversation's.
128
+ */
129
+ oweEnsureRun(conversationId: string, seq: number | string, orgId: string): Promise<boolean>;
123
130
  /** After a root's queue was unpaused: the wakes it held for its tree (agent map spec §5.3). */
124
131
  wakeHeldTree(rootId: string): Promise<void>;
132
+ /**
133
+ * In the transaction that may release a root's hold: owes the ensure-run backstop of each
134
+ * sub-agent {@link wakeHeldTree} wakes after the commit (transactional outbox spec §6).
135
+ */
136
+ oweHeldTreeIn(rootId: string): Promise<void>;
137
+ /**
138
+ * In a decision's transaction, after its decided rows: resumes the waiting run when it waits on
139
+ * nothing else (lock order: conversations, decided rows, runs).
140
+ */
141
+ resumeIn(runId: string, waitId: string): Promise<unknown>;
142
+ /** In a root's delete transaction: owes the stop of its sub-agent tree (`stop-tree`). */
143
+ oweStopDeletedTree(conversationId: string, byAccountId: string | null, orgId: string): Promise<boolean>;
125
144
  /**
126
145
  * 运行是孤儿(会话锁空闲、任务已不存在,没有 worker 会收到中止广播)时直接把它写成 aborted,返回 true;
127
146
  * 否则返回 false。
@@ -222,7 +241,6 @@ export declare class ConversationsService {
222
241
  private readonly runs;
223
242
  /** Resolves a switched-to model and its declared parameters (UI refresh B). */
224
243
  private readonly providers;
225
- private readonly log;
226
244
  /** Reads `@` resources with the sender's credentials. */
227
245
  private readonly content;
228
246
  /** Binds uploaded files to the conversation in the message's transaction. */
@@ -243,7 +261,7 @@ export declare class ConversationsService {
243
261
  /** Starts and stops the conversation's runs. */
244
262
  runs: RunControl,
245
263
  /** Resolves a switched-to model and its declared parameters (UI refresh B). */
246
- providers: Pick<ProviderRegistry, "resolve">, log: Pick<KernelLogger, "warn">,
264
+ providers: Pick<ProviderRegistry, "resolve">,
247
265
  /** Reads `@` resources with the sender's credentials. */
248
266
  content: Pick<ConnectorContent, "resolveAttachments">,
249
267
  /** Binds uploaded files to the conversation in the message's transaction. */
@@ -258,11 +276,6 @@ export declare class ConversationsService {
258
276
  questions: Pick<QuestionsService, "skipPendingIn">,
259
277
  /** ... and its pending network asks (network ask-to-allow spec §6.7). */
260
278
  approvals: Pick<ApprovalsService, "skipNetworkIn">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost);
261
- /**
262
- * 不阻塞地调用一次 `ensureRun`(打开 SSE、读事件时):它幂等,并顺带清理孤儿运行,
263
- * 会话不会因为某次运行结束后的投递失败而永远停在「运行中」。失败只记日志。
264
- */
265
- kickRun(conversationId: string): void;
266
279
  /**
267
280
  * Creates an empty conversation bound to its device (`pick`, see
268
281
  * {@link ConversationDevices.forNew}); an agent the account cannot use →
@@ -297,6 +310,11 @@ export declare class ConversationsService {
297
310
  * the other conversation is never returned).
298
311
  */
299
312
  private findCreated;
313
+ /**
314
+ * The direct `ensureRun` after a message's commit ({@link RunControl.ensureRunAfterCommit}): a
315
+ * failure never fails the request, since the message's transaction owes the ensure-run backstop.
316
+ */
317
+ private startRun;
300
318
  private rowView;
301
319
  /**
302
320
  * Renames a conversation (owner only, as {@link get}): `title_source = 'manual'`, so the title
@@ -399,12 +417,15 @@ export declare class ConversationsService {
399
417
  * only after an access check.
400
418
  */
401
419
  sandboxConversationOf(orgId: string, conversationId: string): Promise<string>;
402
- /** 读事件(先做 `get` 的可见性判定);顺带不阻塞地 {@link kickRun}。 */
420
+ /**
421
+ * 读事件(先做 `get` 的可见性判定). A read never changes run state (transactional outbox spec
422
+ * §6): a run nobody runs is the `run-orphan-check`'s to repair.
423
+ */
403
424
  listEvents(orgId: string, conversationId: string, viewerAccountId: string, afterSeq: number, limit: number): Promise<ConversationEvent[]>;
404
425
  /**
405
426
  * The turn-aligned page of events before `beforeSeq` (`null`: the log's last page), with how
406
427
  * many runs started before it ({@link EventLog.readTurnPageBefore}; history paging spec §2).
407
- * Visibility as {@link get}; kicks a stalled run as {@link listEvents} does.
428
+ * Visibility as {@link get}; a read only, as {@link listEvents}.
408
429
  */
409
430
  listEventsBefore(orgId: string, conversationId: string, viewerAccountId: string, beforeSeq: number | null, limit: number): Promise<{
410
431
  events: ConversationEvent[];
@@ -413,7 +434,8 @@ export declare class ConversationsService {
413
434
  /**
414
435
  * 追加一条用户消息;带 `clientMessageId` 时重复提交返回同一事件。写入后调用 `ensureRun`:
415
436
  * 没有进行中的运行时投递一轮;运行中则由当前运行在步骤间隙吸收,或在它结束时再次 `ensureRun`。
416
- * 投递失败时抛错(消息已写入;客户端带同一 `clientMessageId` 重试会再次触发投递)。
437
+ * A failed `ensureRun` is logged, not thrown: the message's transaction owes the ensure-run
438
+ * backstop, which starts the run once the queue is reachable (transactional outbox spec §6).
417
439
  *
418
440
  * `attachments`(`@` 资源引用):内核以发送者的凭据读取资源(打码、限长),作为用户文本之后的额外文本块写入同一条
419
441
  * user_message,并在 `payload.attachments` 记录元数据(第 i 个附件对应 `content[i + 1]`)。读取失败整条不写。
@@ -452,6 +474,13 @@ export declare class ConversationsService {
452
474
  * conversations (network ask-to-allow spec §6.7). The queue's "send now" uses it as the direct
453
475
  * send does; announce after the commit with {@link announceSkipped}. `toSeq` is the highest seq
454
476
  * written in this conversation (null: none).
477
+ *
478
+ * Lock order: this (root) conversation (the caller's), its pending questions, the sub-agents'
479
+ * conversations with a pending network ask (id order), the asks themselves (the root's and the
480
+ * sub-agents'), and only then the run rows, as each skipped wait's run resumes in this
481
+ * transaction. Every run row comes after every conversation and decided row (ruling R19); a
482
+ * sub-agent's conversation comes after the root's questions, which only this path locks with a
483
+ * sub-agent conversation after them.
455
484
  */
456
485
  skipWaitsIn(conversationId: string, accountId: string, messageSeq: number): Promise<{
457
486
  skipped: ReplySkips;
@@ -460,7 +489,8 @@ export declare class ConversationsService {
460
489
  /**
461
490
  * After the commit: announces each skipped question (`question.answered`, outcome `skipped`) and
462
491
  * network ask (`approval.decided`, status `skipped`; a sub-agent's conversation is notified
463
- * first), so its run resumes (waits) and its IM card updates (channel jobs).
492
+ * first) as domain events with no kernel subscriber: the IM card updates are owed by the skips'
493
+ * appends (transactional outbox spec §8.1); the run resumed in the message's transaction.
464
494
  */
465
495
  announceSkipped(skipped: ReplySkips): Promise<void>;
466
496
  /**
@@ -19,7 +19,6 @@ import { KernelDb } from "../infra/database/kernel-db.js";
19
19
  import { TransactionHost } from "../infra/database/transaction-host.js";
20
20
  import { Transactional } from "../infra/database/transactional.decorator.js";
21
21
  import { KernelEvents } from "../infra/events/kernel-events.js";
22
- import { KERNEL_LOGGER } from "../infra/tokens.js";
23
22
  import { ProviderRegistry } from "../runtime/providers.js";
24
23
  import { RunControl as RunControlService } from "../runtime/run-control.js";
25
24
  import { rootConversationOf } from "../runtime/subagents/child.js";
@@ -130,7 +129,6 @@ let ConversationsService = class ConversationsService {
130
129
  access;
131
130
  runs;
132
131
  providers;
133
- log;
134
132
  content;
135
133
  attachments;
136
134
  invoker;
@@ -144,7 +142,7 @@ let ConversationsService = class ConversationsService {
144
142
  /** Starts and stops the conversation's runs. */
145
143
  runs,
146
144
  /** Resolves a switched-to model and its declared parameters (UI refresh B). */
147
- providers, log,
145
+ providers,
148
146
  /** Reads `@` resources with the sender's credentials. */
149
147
  content,
150
148
  /** Binds uploaded files to the conversation in the message's transaction. */
@@ -165,7 +163,6 @@ let ConversationsService = class ConversationsService {
165
163
  this.access = access;
166
164
  this.runs = runs;
167
165
  this.providers = providers;
168
- this.log = log;
169
166
  this.content = content;
170
167
  this.attachments = attachments;
171
168
  this.invoker = invoker;
@@ -176,15 +173,6 @@ let ConversationsService = class ConversationsService {
176
173
  this.events = events;
177
174
  this.txHost = txHost;
178
175
  }
179
- /**
180
- * 不阻塞地调用一次 `ensureRun`(打开 SSE、读事件时):它幂等,并顺带清理孤儿运行,
181
- * 会话不会因为某次运行结束后的投递失败而永远停在「运行中」。失败只记日志。
182
- */
183
- kickRun(conversationId) {
184
- this.runs
185
- .ensureRun(conversationId)
186
- .catch((err) => this.log.warn("ensureRun on conversation open failed", { conversationId, err }));
187
- }
188
176
  /**
189
177
  * Creates an empty conversation bound to its device (`pick`, see
190
178
  * {@link ConversationDevices.forNew}); an agent the account cannot use →
@@ -237,7 +225,7 @@ let ConversationsService = class ConversationsService {
237
225
  return raced;
238
226
  }
239
227
  await this.eventLog.notify(id, written.seq);
240
- await this.runs.ensureRun(id);
228
+ await this.startRun(id);
241
229
  return { conversation: await this.rowView(id), event: written, events: all, created: true };
242
230
  }
243
231
  /** The conversation row and its first message (undefined: a concurrent retry committed first). */
@@ -292,9 +280,16 @@ let ConversationsService = class ConversationsService {
292
280
  return null;
293
281
  // what the first send wrote: the log from its start up to the message
294
282
  const events = (await this.eventLog.readAfter(row.id, 0, event.seq)).filter((e) => e.seq <= event.seq);
295
- await this.runs.ensureRun(row.id);
283
+ await this.startRun(row.id);
296
284
  return { conversation: toView(row), event, events, created: false };
297
285
  }
286
+ /**
287
+ * The direct `ensureRun` after a message's commit ({@link RunControl.ensureRunAfterCommit}): a
288
+ * failure never fails the request, since the message's transaction owes the ensure-run backstop.
289
+ */
290
+ startRun(conversationId) {
291
+ return this.runs.ensureRunAfterCommit(conversationId);
292
+ }
298
293
  async rowView(id) {
299
294
  return toView(await this.db
300
295
  .selectFrom("conversations")
@@ -352,7 +347,7 @@ let ConversationsService = class ConversationsService {
352
347
  if (!(e instanceof ApiError && e.code === "run.not_active"))
353
348
  throw e;
354
349
  }
355
- await this.markDeleted(conversationId);
350
+ await this.markDeleted(orgId, conversationId, accountId);
356
351
  // after the mark, so no finish or delivery of the stops below can start a run on the deleted
357
352
  // root (`ensureRun` refuses it). The sandbox's tasks stop first, so the sub-agents' tasks end
358
353
  // as a deletion; the tree stop then only stops a task a sub-agent started in between
@@ -362,13 +357,15 @@ let ConversationsService = class ConversationsService {
362
357
  await this.runs.stopTree(conversationId, accountId, { includeSelf: false });
363
358
  await this.eventLog.publishChanged(conversationId, "deleted");
364
359
  }
365
- // the publish happens after the commit, in the caller
366
- async markDeleted(conversationId) {
360
+ // the publish happens after the commit, in the caller; the tree stop after it is owed here
361
+ // (transactional outbox spec §6), so a process that dies before it still stops the tree
362
+ async markDeleted(orgId, conversationId, accountId) {
367
363
  await this.db
368
364
  .updateTable("conversations")
369
365
  .set({ deleted_at: sql `now()` })
370
366
  .where("id", "=", conversationId)
371
367
  .execute();
368
+ await this.runs.oweStopDeletedTree(conversationId, accountId, orgId);
372
369
  // Queued messages must not start a run on a deleted conversation
373
370
  await this.db
374
371
  .deleteFrom("conversation_queue")
@@ -681,26 +678,28 @@ let ConversationsService = class ConversationsService {
681
678
  return conversationId;
682
679
  return rootConversationOf(this.db.db, row.parent_conversation_id);
683
680
  }
684
- /** 读事件(先做 `get` 的可见性判定);顺带不阻塞地 {@link kickRun}。 */
681
+ /**
682
+ * 读事件(先做 `get` 的可见性判定). A read never changes run state (transactional outbox spec
683
+ * §6): a run nobody runs is the `run-orphan-check`'s to repair.
684
+ */
685
685
  async listEvents(orgId, conversationId, viewerAccountId, afterSeq, limit) {
686
686
  await this.get(orgId, conversationId, viewerAccountId);
687
- this.kickRun(conversationId);
688
687
  return this.eventLog.readAfter(conversationId, afterSeq, limit);
689
688
  }
690
689
  /**
691
690
  * The turn-aligned page of events before `beforeSeq` (`null`: the log's last page), with how
692
691
  * many runs started before it ({@link EventLog.readTurnPageBefore}; history paging spec §2).
693
- * Visibility as {@link get}; kicks a stalled run as {@link listEvents} does.
692
+ * Visibility as {@link get}; a read only, as {@link listEvents}.
694
693
  */
695
694
  async listEventsBefore(orgId, conversationId, viewerAccountId, beforeSeq, limit) {
696
695
  await this.get(orgId, conversationId, viewerAccountId);
697
- this.kickRun(conversationId);
698
696
  return this.eventLog.readTurnPageBefore(conversationId, beforeSeq, limit, EVENTS_TURN_EXTEND);
699
697
  }
700
698
  /**
701
699
  * 追加一条用户消息;带 `clientMessageId` 时重复提交返回同一事件。写入后调用 `ensureRun`:
702
700
  * 没有进行中的运行时投递一轮;运行中则由当前运行在步骤间隙吸收,或在它结束时再次 `ensureRun`。
703
- * 投递失败时抛错(消息已写入;客户端带同一 `clientMessageId` 重试会再次触发投递)。
701
+ * A failed `ensureRun` is logged, not thrown: the message's transaction owes the ensure-run
702
+ * backstop, which starts the run once the queue is reachable (transactional outbox spec §6).
704
703
  *
705
704
  * `attachments`(`@` 资源引用):内核以发送者的凭据读取资源(打码、限长),作为用户文本之后的额外文本块写入同一条
706
705
  * user_message,并在 `payload.attachments` 记录元数据(第 i 个附件对应 `content[i + 1]`)。读取失败整条不写。
@@ -719,7 +718,7 @@ let ConversationsService = class ConversationsService {
719
718
  input.clientMessageId !== undefined) {
720
719
  const existing = await this.eventLog.findByIdem(conversationId, userMessageIdemKey(input.clientMessageId));
721
720
  if (existing) {
722
- await this.runs.ensureRun(conversationId);
721
+ await this.startRun(conversationId);
723
722
  return existing;
724
723
  }
725
724
  }
@@ -750,7 +749,7 @@ let ConversationsService = class ConversationsService {
750
749
  .execute();
751
750
  await this.eventLog.publishChanged(conversationId, "archived");
752
751
  }
753
- await this.runs.ensureRun(conversationId);
752
+ await this.startRun(conversationId);
754
753
  return event;
755
754
  }
756
755
  /**
@@ -812,10 +811,19 @@ let ConversationsService = class ConversationsService {
812
811
  * conversations (network ask-to-allow spec §6.7). The queue's "send now" uses it as the direct
813
812
  * send does; announce after the commit with {@link announceSkipped}. `toSeq` is the highest seq
814
813
  * written in this conversation (null: none).
814
+ *
815
+ * Lock order: this (root) conversation (the caller's), its pending questions, the sub-agents'
816
+ * conversations with a pending network ask (id order), the asks themselves (the root's and the
817
+ * sub-agents'), and only then the run rows, as each skipped wait's run resumes in this
818
+ * transaction. Every run row comes after every conversation and decided row (ruling R19); a
819
+ * sub-agent's conversation comes after the root's questions, which only this path locks with a
820
+ * sub-agent conversation after them.
815
821
  */
816
822
  async skipWaitsIn(conversationId, accountId, messageSeq) {
817
823
  const q = await this.questions.skipPendingIn(conversationId, accountId, messageSeq);
818
824
  const n = await this.approvals.skipNetworkIn(conversationId, accountId);
825
+ for (const w of [...q.records, ...n.records])
826
+ await this.runs.resumeIn(w.runId, w.id);
819
827
  const here = n.seqs.get(conversationId);
820
828
  const toSeq = q.toSeq === null && here === undefined ? null : Math.max(q.toSeq ?? 0, here ?? 0);
821
829
  return { skipped: { questions: q.records, network: n.records, seqs: n.seqs }, toSeq };
@@ -823,7 +831,8 @@ let ConversationsService = class ConversationsService {
823
831
  /**
824
832
  * After the commit: announces each skipped question (`question.answered`, outcome `skipped`) and
825
833
  * network ask (`approval.decided`, status `skipped`; a sub-agent's conversation is notified
826
- * first), so its run resumes (waits) and its IM card updates (channel jobs).
834
+ * first) as domain events with no kernel subscriber: the IM card updates are owed by the skips'
835
+ * appends (transactional outbox spec §8.1); the run resumed in the message's transaction.
827
836
  */
828
837
  async announceSkipped(skipped) {
829
838
  for (const q of skipped.questions) {
@@ -877,6 +886,10 @@ let ConversationsService = class ConversationsService {
877
886
  events.push(this.userMessage(accountId, input, extra, blocks, invoked, context, internal.schedule));
878
887
  const { events: written, fresh } = await this.eventLog.appendInReport(conversationId, events);
879
888
  const message = written.at(-1);
889
+ // the run this message starts is owed with it: the direct ensureRun after the commit may never
890
+ // happen (the process died) or fail (transactional outbox spec §6)
891
+ if (fresh.at(-1) === true)
892
+ await this.runs.oweEnsureRun(conversationId, message.seq, orgId);
880
893
  const skips = fresh.at(-1) === true
881
894
  ? await this.skipWaitsIn(conversationId, accountId, message.seq)
882
895
  : { skipped: NO_REPLY_SKIPS, toSeq: null };
@@ -995,13 +1008,15 @@ let ConversationsService = class ConversationsService {
995
1008
  throw new ApiError("run.not_active", "No active run to abort");
996
1009
  const runs = this.runs;
997
1010
  // 等待中:直接结束;没成功(刚被续跑、已回到 queued)则按下面的常规路径中止。
1011
+ // the stop's terminal transaction owed the ensure-run backstop; the direct call starts what
1012
+ // waited for the stopped run now
998
1013
  if (run.status === "waiting" && (await runs.abortWaiting(conversationId, run.id, accountId))) {
999
- this.kickRun(conversationId);
1014
+ await runs.ensureRunAfterCommit(conversationId);
1000
1015
  return run.id;
1001
1016
  }
1002
1017
  // 孤儿运行没有 worker 在听中止广播:直接写成 aborted,之后再看有没有等待中的消息要起新一轮。
1003
1018
  if (await runs.abortIfOrphaned(conversationId, run.id, accountId)) {
1004
- this.kickRun(conversationId);
1019
+ await runs.ensureRunAfterCommit(conversationId);
1005
1020
  return run.id;
1006
1021
  }
1007
1022
  await runs.requestAbort(run.id, accountId);
@@ -1034,15 +1049,14 @@ ConversationsService = __decorate([
1034
1049
  __param(3, Inject(ConversationAccess)),
1035
1050
  __param(4, Inject(RunControlService)),
1036
1051
  __param(5, Inject(ProviderRegistry)),
1037
- __param(6, Inject(KERNEL_LOGGER)),
1038
- __param(7, Inject(ConnectorContent)),
1039
- __param(8, Inject(AttachmentsService)),
1040
- __param(9, Inject(SkillInvoker)),
1041
- __param(10, Inject(BackgroundTaskStopper)),
1042
- __param(11, Inject(ConversationDevices)),
1043
- __param(12, Inject(QuestionsService)),
1044
- __param(13, Inject(ApprovalsService)),
1045
- __param(14, Inject(KernelEvents)),
1046
- __param(15, Inject(TransactionHost))
1052
+ __param(6, Inject(ConnectorContent)),
1053
+ __param(7, Inject(AttachmentsService)),
1054
+ __param(8, Inject(SkillInvoker)),
1055
+ __param(9, Inject(BackgroundTaskStopper)),
1056
+ __param(10, Inject(ConversationDevices)),
1057
+ __param(11, Inject(QuestionsService)),
1058
+ __param(12, Inject(ApprovalsService)),
1059
+ __param(13, Inject(KernelEvents)),
1060
+ __param(14, Inject(TransactionHost))
1047
1061
  ], ConversationsService);
1048
1062
  export { ConversationsService };
@@ -1,6 +1,6 @@
1
1
  import { KernelDb } from "../infra/database/kernel-db.js";
2
2
  import { TransactionHost } from "../infra/database/transaction-host.js";
3
- import { KernelEvents } from "../infra/events/kernel-events.js";
3
+ import { OutboxService } from "../infra/outbox/outbox.service.js";
4
4
  import type { KernelLogger } from "../log.js";
5
5
  import { type ConversationEvent, type ConversationEventType } from "./events.js";
6
6
  /** 事件日志依赖的总线:内核 bus(命名空间 `kernel`)。 */
@@ -74,17 +74,17 @@ export type ConversationChange = (typeof CONVERSATION_CHANGES)[number];
74
74
  * seq 由 `conversations.last_seq` 在会话行锁内连续分配(多副本并发写同一会话时串行化);
75
75
  * 幂等键的「先查后写」也在同一把行锁内完成。
76
76
  *
77
- * Every published advance (`notify`) is also announced in-process as `conversation.event-appended`
78
- * on {@link KernelEvents} (the channel outbound delivery enqueues from it), on this replica, right
79
- * after the bus publish, in append order.
77
+ * An append to a conversation with a channel target (`channel_deliveries`, read with the row lock)
78
+ * owes the conversation's IM delivery in the append's transaction (transactional outbox spec
79
+ * §8.1); a conversation without one writes nothing more.
80
80
  */
81
81
  export declare class EventLog {
82
82
  private readonly db;
83
83
  private readonly bus;
84
84
  private readonly log;
85
- private readonly events;
85
+ private readonly outbox;
86
86
  readonly txHost: TransactionHost;
87
- constructor(db: KernelDb, bus: EventBus, log: Pick<KernelLogger, "warn">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost);
87
+ constructor(db: KernelDb, bus: EventBus, log: Pick<KernelLogger, "warn">, outbox: Pick<OutboxService, "add">, txHost: TransactionHost);
88
88
  /**
89
89
  * 追加事件,返回与输入一一对应的事件(幂等命中的为已有事件)。
90
90
  * 自开一个独立事务(`requires_new`,与调用方的事务无关),提交后发布 `conv.<id>`。
@@ -107,10 +107,7 @@ export declare class EventLog {
107
107
  fresh: boolean[];
108
108
  }>;
109
109
  private appendOwn;
110
- /**
111
- * 发布「日志已推进到 toSeq」;发布失败只记日志(事件已提交,订阅方可按 seq 补读)。Then announces
112
- * it in-process (`conversation.event-appended`); a failing subscriber is logged by KernelEvents.
113
- */
110
+ /** 发布「日志已推进到 toSeq」;发布失败只记日志(事件已提交,订阅方可按 seq 补读)。 */
114
111
  notify(conversationId: string, toSeq: number): Promise<void>;
115
112
  /**
116
113
  * Publishes a `changed` frame on the conversation topic (rename, generated title, selection, ...).