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

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 (198) hide show
  1. package/dist/admin/admin.module.js +2 -0
  2. package/dist/admin/health.service.d.ts +2 -2
  3. package/dist/admin/health.service.js +6 -2
  4. package/dist/admin/outbox-dead.service.d.ts +7 -3
  5. package/dist/admin/outbox-dead.service.js +17 -6
  6. package/dist/admin/overview.service.d.ts +6 -6
  7. package/dist/admin/overview.service.js +7 -7
  8. package/dist/agents/agents.module.js +3 -0
  9. package/dist/agents/agents.service.d.ts +6 -1
  10. package/dist/agents/agents.service.js +11 -2
  11. package/dist/background/wake-rules.d.ts +5 -0
  12. package/dist/background/wake-rules.js +16 -5
  13. package/dist/catalog/catalog-import.module.js +0 -4
  14. package/dist/catalog/catalog-scope.service.d.ts +63 -26
  15. package/dist/catalog/catalog-scope.service.js +146 -71
  16. package/dist/catalog/catalog.module.js +4 -3
  17. package/dist/catalog/catalog.service.d.ts +11 -4
  18. package/dist/catalog/catalog.service.js +21 -5
  19. package/dist/catalog/cli.d.ts +4 -3
  20. package/dist/catalog/cli.js +8 -13
  21. package/dist/catalog/job-ids.d.ts +8 -20
  22. package/dist/catalog/job-ids.js +11 -16
  23. package/dist/catalog/marketplaces.service.d.ts +1 -1
  24. package/dist/catalog/org-plugin-uploads.d.ts +11 -4
  25. package/dist/catalog/org-plugin-uploads.js +21 -6
  26. package/dist/catalog/owned-items.d.ts +6 -13
  27. package/dist/catalog/owned-items.js +11 -29
  28. package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
  29. package/dist/catalog/owned-plugin-cleanup.js +12 -66
  30. package/dist/catalog/platform-components.d.ts +56 -34
  31. package/dist/catalog/platform-components.js +68 -78
  32. package/dist/catalog/plugin-import.d.ts +28 -25
  33. package/dist/catalog/plugin-import.js +90 -76
  34. package/dist/catalog/plugin-upgrade.d.ts +4 -4
  35. package/dist/catalog/plugin-upgrade.js +7 -19
  36. package/dist/catalog/scope-jobs.d.ts +59 -0
  37. package/dist/catalog/scope-jobs.js +65 -0
  38. package/dist/catalog/scope-sync-workers.d.ts +22 -0
  39. package/dist/catalog/scope-sync-workers.js +58 -2
  40. package/dist/catalog/scope-sync.d.ts +40 -65
  41. package/dist/catalog/scope-sync.js +114 -122
  42. package/dist/channels/channels.module.d.ts +9 -8
  43. package/dist/channels/channels.module.js +11 -10
  44. package/dist/channels/connections.service.d.ts +3 -3
  45. package/dist/channels/connections.service.js +6 -6
  46. package/dist/channels/deliver-job.d.ts +10 -3
  47. package/dist/channels/deliver-job.js +7 -3
  48. package/dist/channels/inbound.js +2 -2
  49. package/dist/channels/jobs.d.ts +11 -4
  50. package/dist/channels/jobs.js +18 -3
  51. package/dist/channels/outbound.d.ts +5 -2
  52. package/dist/channels/outbound.js +9 -2
  53. package/dist/channels/threads.d.ts +4 -3
  54. package/dist/channels/threads.js +4 -3
  55. package/dist/conversations/conversations.controller.d.ts +4 -2
  56. package/dist/conversations/conversations.controller.js +8 -4
  57. package/dist/conversations/conversations.service.d.ts +54 -9
  58. package/dist/conversations/conversations.service.js +94 -25
  59. package/dist/conversations/events.d.ts +27 -0
  60. package/dist/conversations/events.js +30 -1
  61. package/dist/conversations/source.d.ts +10 -0
  62. package/dist/conversations/source.js +17 -0
  63. package/dist/conversations/tree.d.ts +10 -0
  64. package/dist/conversations/tree.js +35 -0
  65. package/dist/db/kernel-schema.d.ts +19 -15
  66. package/dist/devices/devices.service.d.ts +25 -14
  67. package/dist/devices/devices.service.js +36 -30
  68. package/dist/evals/evals.module.js +5 -1
  69. package/dist/evals/jobs.d.ts +14 -6
  70. package/dist/evals/jobs.js +21 -19
  71. package/dist/evals/run-replay.d.ts +13 -2
  72. package/dist/evals/run-replay.js +28 -1
  73. package/dist/evals/runs.service.d.ts +5 -6
  74. package/dist/evals/runs.service.js +11 -33
  75. package/dist/evals/snapshot.d.ts +7 -1
  76. package/dist/evals/snapshot.js +2 -1
  77. package/dist/extensions/hooks.d.ts +22 -3
  78. package/dist/extensions/hooks.js +15 -1
  79. package/dist/extensions/lifecycle-hooks.d.ts +99 -0
  80. package/dist/extensions/lifecycle-hooks.js +273 -0
  81. package/dist/extensions/org-config.d.ts +13 -1
  82. package/dist/extensions/org-config.js +19 -2
  83. package/dist/extensions/permission-view.d.ts +9 -5
  84. package/dist/extensions/permission-view.js +9 -4
  85. package/dist/extensions/point-defaults.d.ts +28 -1
  86. package/dist/extensions/point-defaults.js +44 -13
  87. package/dist/inbox/inbox.service.d.ts +6 -20
  88. package/dist/inbox/inbox.service.js +0 -3
  89. package/dist/inbox/org-notices.d.ts +1 -1
  90. package/dist/inbox/platform-inbox.service.d.ts +3 -32
  91. package/dist/inbox/platform-inbox.service.js +5 -54
  92. package/dist/infra/outbox/outbox.service.d.ts +6 -0
  93. package/dist/infra/outbox/outbox.service.js +51 -17
  94. package/dist/infra/outbox/registry.d.ts +176 -0
  95. package/dist/infra/outbox/registry.js +183 -0
  96. package/dist/infra/queue/queue-names.d.ts +1 -1
  97. package/dist/infra/queue/queue-names.js +1 -0
  98. package/dist/local-devices/device-policy.service.d.ts +131 -26
  99. package/dist/local-devices/device-policy.service.js +341 -145
  100. package/dist/local-devices/machines.d.ts +17 -2
  101. package/dist/local-devices/machines.js +66 -5
  102. package/dist/local-devices/policy-op-jobs.d.ts +15 -0
  103. package/dist/local-devices/policy-op-jobs.js +12 -0
  104. package/dist/local-devices/policy-summary.d.ts +36 -3
  105. package/dist/local-devices/policy-summary.js +47 -1
  106. package/dist/orgs/directory-sync.js +1 -1
  107. package/dist/orgs/members.service.js +1 -1
  108. package/dist/orgs/offboarding.d.ts +50 -57
  109. package/dist/orgs/offboarding.js +117 -162
  110. package/dist/orgs/orgs.module.js +3 -0
  111. package/dist/orgs/orgs.service.d.ts +11 -5
  112. package/dist/orgs/orgs.service.js +19 -7
  113. package/dist/overrides.d.ts +7 -0
  114. package/dist/overrides.js +4 -0
  115. package/dist/platform/ops/conversations.ops.js +1 -1
  116. package/dist/platform/ops/inbox.ops.js +4 -20
  117. package/dist/platform/ops/platform.ops.d.ts +1 -0
  118. package/dist/platform/ops/platform.ops.js +6 -3
  119. package/dist/roles/worker-role.d.ts +10 -5
  120. package/dist/roles/worker-role.js +19 -9
  121. package/dist/roles/worker.module.js +2 -4
  122. package/dist/runtime/context/build.d.ts +3 -0
  123. package/dist/runtime/context/build.js +15 -0
  124. package/dist/runtime/context/plugin-context.d.ts +20 -0
  125. package/dist/runtime/context/plugin-context.js +30 -0
  126. package/dist/runtime/langgraph-loop.js +4 -0
  127. package/dist/runtime/loop.d.ts +8 -0
  128. package/dist/runtime/maintenance.d.ts +9 -4
  129. package/dist/runtime/maintenance.js +52 -13
  130. package/dist/runtime/model-call.d.ts +6 -1
  131. package/dist/runtime/model-call.js +1 -0
  132. package/dist/runtime/plugin-context.service.d.ts +75 -0
  133. package/dist/runtime/plugin-context.service.js +220 -0
  134. package/dist/runtime/run-context.service.d.ts +7 -3
  135. package/dist/runtime/run-context.service.js +12 -2
  136. package/dist/runtime/run-control.d.ts +6 -1
  137. package/dist/runtime/run-control.js +13 -0
  138. package/dist/runtime/run-finish.d.ts +2 -2
  139. package/dist/runtime/run-finish.js +11 -6
  140. package/dist/runtime/run-jobs.d.ts +21 -4
  141. package/dist/runtime/run-jobs.js +29 -6
  142. package/dist/runtime/run-orphan-check.job.d.ts +14 -11
  143. package/dist/runtime/run-orphan-check.job.js +16 -11
  144. package/dist/runtime/run-queue.d.ts +29 -24
  145. package/dist/runtime/run-queue.js +118 -57
  146. package/dist/runtime/run-status.d.ts +12 -0
  147. package/dist/runtime/run-status.js +21 -0
  148. package/dist/runtime/run-worker.d.ts +32 -3
  149. package/dist/runtime/run-worker.js +143 -21
  150. package/dist/runtime/runtime.module.js +2 -0
  151. package/dist/runtime/step-log.d.ts +6 -5
  152. package/dist/runtime/step-log.js +6 -5
  153. package/dist/runtime/subagents/dispatch.js +1 -0
  154. package/dist/runtime/subagents/skill-fork.js +3 -0
  155. package/dist/runtime/usage-report.d.ts +3 -1
  156. package/dist/runtime/usage-report.js +4 -2
  157. package/dist/runtime/waits.d.ts +16 -6
  158. package/dist/runtime/waits.js +37 -15
  159. package/dist/sandbox/jobs.d.ts +20 -10
  160. package/dist/sandbox/jobs.js +23 -17
  161. package/dist/schedules/fire.d.ts +25 -4
  162. package/dist/schedules/fire.js +87 -13
  163. package/dist/schedules/job-ids.d.ts +66 -17
  164. package/dist/schedules/job-ids.js +81 -13
  165. package/dist/schedules/run-tracker.js +2 -1
  166. package/dist/schedules/scan.job.d.ts +16 -35
  167. package/dist/schedules/scan.job.js +38 -147
  168. package/dist/schedules/scheduled-tasks.service.d.ts +6 -6
  169. package/dist/schedules/scheduled-tasks.service.js +17 -14
  170. package/dist/schedules/schedules.module.js +0 -4
  171. package/dist/skills/catalog.d.ts +7 -3
  172. package/dist/skills/catalog.js +12 -2
  173. package/dist/skills/changes.controller.d.ts +1 -1
  174. package/dist/skills/changes.controller.js +1 -1
  175. package/dist/tools/approvals.service.d.ts +111 -23
  176. package/dist/tools/approvals.service.js +326 -77
  177. package/dist/tools/builtins/sandbox/task-output.js +21 -6
  178. package/dist/tools/device-op-resolver.d.ts +40 -0
  179. package/dist/tools/device-op-resolver.js +121 -0
  180. package/dist/tools/network-asks.d.ts +1 -1
  181. package/dist/tools/network-asks.js +4 -3
  182. package/dist/tools/pipeline.d.ts +11 -2
  183. package/dist/tools/pipeline.js +5 -0
  184. package/dist/tools/questions.service.d.ts +15 -4
  185. package/dist/tools/questions.service.js +21 -19
  186. package/dist/tools/registry.d.ts +8 -1
  187. package/dist/tools/registry.js +9 -0
  188. package/dist/tools/tools.module.js +4 -0
  189. package/package.json +3 -3
  190. package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
  191. package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
  192. package/src/migrations/0123_pgvector.sql +27 -0
  193. package/src/migrations/0124_conversation_source.sql +36 -0
  194. package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
  195. package/dist/catalog/platform-migration.d.ts +0 -115
  196. package/dist/catalog/platform-migration.js +0 -583
  197. package/dist/infra/queue/add-job-once.d.ts +0 -14
  198. package/dist/infra/queue/add-job-once.js +0 -16
@@ -4,15 +4,22 @@ import type { OutboxService } from "../infra/outbox/outbox.service.js";
4
4
  * with no BullMQ and no Nest class, so the event log can owe it without importing the channels
5
5
  * domain. `jobs.ts` (the producer) re-exports these names.
6
6
  */
7
- /** 渠道队列(出站投递与入站任务,不含 `:`),只在 worker 角色上消费。 */
7
+ /** The channel queue (outbound deliveries and inbound jobs; no `:`), consumed only by workers. */
8
8
  export declare const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
9
- /** 投递任务名。 */
9
+ /** The delivery job's name. */
10
10
  export declare const DELIVER_JOB = "deliver";
11
- /** 投递任务 id(不含 `:`):同一会话、同一日志位置只排一个任务。 */
11
+ /** The delivery job id (no `:`): one job per conversation and log position. */
12
12
  export declare const deliverJobId: (conversationId: string, seq: number) => string;
13
13
  export interface DeliverJobData {
14
14
  conversationId: string;
15
15
  }
16
+ /** The catch-up job of a connection that can deliver again (re-enabled, credentials rotated). */
17
+ export declare const RESUME_JOB = "channel-resume";
18
+ /** Its job id: one per connection and update (`updated_at` in ms; no `:`). */
19
+ export declare const resumeJobId: (connectionId: string, at: Date) => string;
20
+ export interface ResumeJobData {
21
+ connectionId: string;
22
+ }
16
23
  /**
17
24
  * Owes the conversation's delivery job for the log position `seq` in the transaction in context:
18
25
  * the caller knows the conversation has a channel target. Shares the job id of
@@ -3,12 +3,16 @@
3
3
  * with no BullMQ and no Nest class, so the event log can owe it without importing the channels
4
4
  * domain. `jobs.ts` (the producer) re-exports these names.
5
5
  */
6
- /** 渠道队列(出站投递与入站任务,不含 `:`),只在 worker 角色上消费。 */
6
+ /** The channel queue (outbound deliveries and inbound jobs; no `:`), consumed only by workers. */
7
7
  export const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
8
- /** 投递任务名。 */
8
+ /** The delivery job's name. */
9
9
  export const DELIVER_JOB = "deliver";
10
- /** 投递任务 id(不含 `:`):同一会话、同一日志位置只排一个任务。 */
10
+ /** The delivery job id (no `:`): one job per conversation and log position. */
11
11
  export const deliverJobId = (conversationId, seq) => `deliver-${conversationId}-${seq}`;
12
+ /** The catch-up job of a connection that can deliver again (re-enabled, credentials rotated). */
13
+ export const RESUME_JOB = "channel-resume";
14
+ /** Its job id: one per connection and update (`updated_at` in ms; no `:`). */
15
+ export const resumeJobId = (connectionId, at) => `channel-resume-${connectionId}-${at.getTime()}`;
12
16
  /**
13
17
  * Owes the conversation's delivery job for the log position `seq` in the transaction in context:
14
18
  * the caller knows the conversation has a channel target. Shares the job id of
@@ -11,6 +11,7 @@ import { randomUUID } from "node:crypto";
11
11
  import { Inject, Injectable } from "@nestjs/common";
12
12
  import { ConversationsService } from "../conversations/conversations.service.js";
13
13
  import { imSenderContext, recipientLocale, } from "../conversations/message-context.js";
14
+ import { channelSource } from "../conversations/source.js";
14
15
  import { DevicesService } from "../devices/devices.service.js";
15
16
  import { DEFAULT_DEVICE_ALIASES, deviceLabel } from "../devices/label.js";
16
17
  import { ApiError } from "../errors.js";
@@ -227,8 +228,7 @@ let ChannelInbound = class ChannelInbound {
227
228
  try {
228
229
  // bound to the person's IM default device, else their default (environments spec
229
230
  // §4.2 item 8); an anonymous sender's stays unbound (no sandbox)
230
- created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }))
231
- .id;
231
+ created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }, { source: channelSource(e.target.chatType) })).id;
232
232
  }
233
233
  catch (err) {
234
234
  if (err instanceof ApiError && err.code === "agent.not_found") {
@@ -5,8 +5,8 @@ import { KernelDb } from "../infra/database/kernel-db.js";
5
5
  import { TransactionHost } from "../infra/database/transaction-host.js";
6
6
  import { KernelEvents } from "../infra/events/kernel-events.js";
7
7
  import { OutboxService } from "../infra/outbox/outbox.service.js";
8
- import { type DeliverJobData } from "./deliver-job.js";
9
- export { CHANNEL_QUEUE_NAME, DELIVER_JOB, type DeliverJobData, deliverJobId, oweDelivery, } from "./deliver-job.js";
8
+ import { type DeliverJobData, type ResumeJobData } from "./deliver-job.js";
9
+ export { CHANNEL_QUEUE_NAME, DELIVER_JOB, type DeliverJobData, deliverJobId, oweDelivery, RESUME_JOB, type ResumeJobData, resumeJobId, } from "./deliver-job.js";
10
10
  /**
11
11
  * 结束后复核排的任务 id:每次唯一(带随机后缀,不含 `:`)。不能复用 `deliver-<会话>-<seq>`——同 id 的任务
12
12
  * 可能正因锁被占而即将直接结束,BullMQ 会把新任务去重进它,于是没人投递。
@@ -24,7 +24,7 @@ export interface InboundJobData {
24
24
  connectionId: string;
25
25
  event: InboundEvent;
26
26
  }
27
- export type ChannelJobData = DeliverJobData | InboundJobData;
27
+ export type ChannelJobData = DeliverJobData | InboundJobData | ResumeJobData;
28
28
  /** 入站任务名。 */
29
29
  export declare const INBOUND_JOB = "inbound";
30
30
  /**
@@ -86,7 +86,14 @@ export declare class ChannelJobs implements OnApplicationShutdown {
86
86
  */
87
87
  enqueueInbound(connectionId: string, event: InboundEvent, jobId: string): Promise<void>;
88
88
  /**
89
- * 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
89
+ * Owes the catch-up of a connection that can deliver again in the caller's transaction (the
90
+ * update that re-enabled it or rotated its credentials while active): the deliveries that ran
91
+ * while it could not deliver have ended, and nothing else finds the conversations lagging the log.
92
+ * `at` is the connection row's new `updated_at`.
93
+ */
94
+ oweResume(connectionId: string, orgId: string, at: Date): Promise<boolean>;
95
+ /**
96
+ * The {@link RESUME_JOB} job: 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
90
97
  * 补投停用或凭据失效期间落下的内容(不必等下一条消息)。
91
98
  */
92
99
  resumeConnection(connectionId: string): Promise<void>;
@@ -17,8 +17,8 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
17
17
  import { OutboxService } from "../infra/outbox/outbox.service.js";
18
18
  import { OUTBOX_JOBS } from "../infra/outbox/registry.js";
19
19
  import { KERNEL_LOGGER } from "../infra/tokens.js";
20
- import { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, } from "./deliver-job.js";
21
- export { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, } from "./deliver-job.js";
20
+ import { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, RESUME_JOB, resumeJobId, } from "./deliver-job.js";
21
+ export { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, RESUME_JOB, resumeJobId, } from "./deliver-job.js";
22
22
  /**
23
23
  * 结束后复核排的任务 id:每次唯一(带随机后缀,不含 `:`)。不能复用 `deliver-<会话>-<seq>`——同 id 的任务
24
24
  * 可能正因锁被占而即将直接结束,BullMQ 会把新任务去重进它,于是没人投递。
@@ -160,7 +160,22 @@ let ChannelJobs = class ChannelJobs {
160
160
  });
161
161
  }
162
162
  /**
163
- * 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
163
+ * Owes the catch-up of a connection that can deliver again in the caller's transaction (the
164
+ * update that re-enabled it or rotated its credentials while active): the deliveries that ran
165
+ * while it could not deliver have ended, and nothing else finds the conversations lagging the log.
166
+ * `at` is the connection row's new `updated_at`.
167
+ */
168
+ oweResume(connectionId, orgId, at) {
169
+ return this.outbox.add({
170
+ queue: CHANNEL_QUEUE_NAME,
171
+ name: RESUME_JOB,
172
+ jobId: resumeJobId(connectionId, at),
173
+ data: { connectionId },
174
+ orgId,
175
+ });
176
+ }
177
+ /**
178
+ * The {@link RESUME_JOB} job: 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
164
179
  * 补投停用或凭据失效期间落下的内容(不必等下一条消息)。
165
180
  */
166
181
  async resumeConnection(connectionId) {
@@ -97,7 +97,7 @@ export declare class ChannelOutbound {
97
97
  /** The conversation lock provider; delivery takes it under its own key prefix. */
98
98
  lock: Pick<ConversationLock, "scoped">, connections: Pick<ChannelConnections, "outbound">, approvals: Pick<ApprovalsService, "describe">, questions: Pick<QuestionsService, "forCall">, registry: Pick<ToolRegistry, "builtinSpecs" | "extensionToolSpecs">,
99
99
  /** 入站任务的处理(worker 上执行 `ChannelInbound.handle`);失败抛错由 BullMQ 重试。 */
100
- inbound: Pick<ChannelInbound, "handle">, jobs: Pick<ChannelJobs, "enqueue">, config: Pick<KernelConfigService, "get">, log: Pick<Logger, "error" | "warn" | "info">, txHost: TransactionHost,
100
+ inbound: Pick<ChannelInbound, "handle">, jobs: Pick<ChannelJobs, "enqueue" | "resumeConnection">, config: Pick<KernelConfigService, "get">, log: Pick<Logger, "error" | "warn" | "info">, txHost: TransactionHost,
101
101
  /** Pauses the task of a scheduled run whose chat is gone (scheduled tasks spec §8). */
102
102
  schedules: Pick<ScheduledTasksService, "pauseFor">);
103
103
  /**
@@ -112,7 +112,10 @@ export declare class ChannelOutbound {
112
112
  /** Stops: the jobs in flight are handed back at their next checkpoint, the worker closes. */
113
113
  close(): Promise<void>;
114
114
  private requeue;
115
- /** One job of the channel queue (the `@Processor` calls it): a delivery or an inbound event. */
115
+ /**
116
+ * One job of the channel queue (the `@Processor` calls it): a delivery, an inbound event or a
117
+ * connection's catch-up.
118
+ */
116
119
  process(job: Job<ChannelJobData>, token: string | undefined): Promise<void>;
117
120
  /** 本副本没有加载连接的插件:推迟重试(不消耗重试次数),排队超过上限才放弃。 */
118
121
  private pluginPending;
@@ -28,7 +28,7 @@ import { ToolRegistry } from "../tools/registry.js";
28
28
  import { approvalCard, cardMessage, connectUrl, conversationUrl, questionCard, webBase, } from "./cards.js";
29
29
  import { ChannelConnections } from "./connections.service.js";
30
30
  import { ChannelInbound } from "./inbound.js";
31
- import { ChannelJobs, INBOUND_JOB, idempotencyKey, } from "./jobs.js";
31
+ import { ChannelJobs, INBOUND_JOB, idempotencyKey, RESUME_JOB, } from "./jobs.js";
32
32
  import { phrase } from "./phrases.js";
33
33
  import { insertThread } from "./threads.js";
34
34
  // The queue, job names, ids and priorities live with the producer (jobs.ts)
@@ -233,12 +233,19 @@ let ChannelOutbound = class ChannelOutbound {
233
233
  await job.moveToDelayed(Date.now() + delayMs, token);
234
234
  throw new DelayedError();
235
235
  }
236
- /** One job of the channel queue (the `@Processor` calls it): a delivery or an inbound event. */
236
+ /**
237
+ * One job of the channel queue (the `@Processor` calls it): a delivery, an inbound event or a
238
+ * connection's catch-up.
239
+ */
237
240
  async process(job, token) {
238
241
  if (this.stopping.signal.aborted)
239
242
  return this.requeue(job, token, 0);
240
243
  if (job.name === INBOUND_JOB)
241
244
  return this.processInbound(job, token);
245
+ // a connection's catch-up: a failed add fails the job, which BullMQ retries
246
+ if (job.name === RESUME_JOB) {
247
+ return this.jobs.resumeConnection(job.data.connectionId);
248
+ }
242
249
  return this.deliver(job, token);
243
250
  }
244
251
  /** 本副本没有加载连接的插件:推迟重试(不消耗重试次数),排队超过上限才放弃。 */
@@ -21,9 +21,10 @@ export declare class AnonymousGuests {
21
21
  readonly txHost: TransactionHost;
22
22
  constructor(db: KernelDb, guestLock: AnonymousGuestLock, txHost: TransactionHost);
23
23
  /**
24
- * 该组织唯一的匿名访客账号 id;首次使用时创建(`accounts.status = active`、没有任何登录方式、组织角色 guest)。
25
- * 每次都确保成员关系存在(被移出后再次使用会补回 guest):运行的兜底扫描会中止「发起人已不是该组织成员」的运行。
26
- * 跨副本并发首次创建由 {@link AnonymousGuestLock} 串行化,只建一个账号。
24
+ * The org's one anonymous guest account id, created on first use (`accounts.status = active`, no
25
+ * way to sign in, org role guest). Every call makes sure the membership exists (a removed guest
26
+ * is added back on its next use). Concurrent first creations across replicas are serialized by
27
+ * {@link AnonymousGuestLock}: one account.
27
28
  */
28
29
  ensure(orgId: string): Promise<string>;
29
30
  }
@@ -55,9 +55,10 @@ let AnonymousGuests = class AnonymousGuests {
55
55
  this.txHost = txHost;
56
56
  }
57
57
  /**
58
- * 该组织唯一的匿名访客账号 id;首次使用时创建(`accounts.status = active`、没有任何登录方式、组织角色 guest)。
59
- * 每次都确保成员关系存在(被移出后再次使用会补回 guest):运行的兜底扫描会中止「发起人已不是该组织成员」的运行。
60
- * 跨副本并发首次创建由 {@link AnonymousGuestLock} 串行化,只建一个账号。
58
+ * The org's one anonymous guest account id, created on first use (`accounts.status = active`, no
59
+ * way to sign in, org role guest). Every call makes sure the membership exists (a removed guest
60
+ * is added back on its next use). Concurrent first creations across replicas are serialized by
61
+ * {@link AnonymousGuestLock}: one account.
61
62
  */
62
63
  async ensure(orgId) {
63
64
  await this.guestLock.lock(orgId);
@@ -312,8 +312,10 @@ export declare class ConversationsController {
312
312
  }>;
313
313
  /**
314
314
  * Stops the current run (the conversation's owner): 202 `{ runId }`; nothing active → 409
315
- * `run.not_active`. On a root conversation `runId` is the stopped run, and its background
316
- * sub-agents keep working. On a sub-agent's conversation (the root's owner, at any depth) the
315
+ * `run.not_active`. On a root conversation every active sub-agent of its tree stops with its
316
+ * run, and nothing they deliver starts the root again until its next run (agent map spec §6,
317
+ * changed in acceptance 2026-11-13); `runId` is the stopped run (the latest when only sub-agents
318
+ * ran). On a sub-agent's conversation (the root's owner, at any depth) the
317
319
  * sub-agent stops with every sub-agent below it, and `runId` is the sub-agent's latest run,
318
320
  * which may already be finished when only its descendants were working (agent map spec §6).
319
321
  */
@@ -190,7 +190,9 @@ let ConversationsController = class ConversationsController {
190
190
  const pick = body.deviceId === undefined ? {} : { deviceId: body.deviceId };
191
191
  if (body.message === undefined) {
192
192
  return {
193
- conversation: await this.conversations.create(org.orgId, p.account.id, body.agentId, pick),
193
+ conversation: await this.conversations.create(org.orgId, p.account.id, body.agentId, pick, {
194
+ source: "web",
195
+ }),
194
196
  };
195
197
  }
196
198
  const m = body.message;
@@ -204,7 +206,7 @@ let ConversationsController = class ConversationsController {
204
206
  ...(m.skill !== undefined && { skill: m.skill }),
205
207
  ...(m.timeZone !== undefined && { timeZone: m.timeZone }),
206
208
  ...(m.locale !== undefined && { locale: m.locale }),
207
- }, pick);
209
+ }, pick, { source: "web" });
208
210
  reply.status(out.created ? 201 : 200);
209
211
  return { conversation: out.conversation, event: out.event, events: out.events };
210
212
  }
@@ -454,8 +456,10 @@ let ConversationsController = class ConversationsController {
454
456
  }
455
457
  /**
456
458
  * Stops the current run (the conversation's owner): 202 `{ runId }`; nothing active → 409
457
- * `run.not_active`. On a root conversation `runId` is the stopped run, and its background
458
- * sub-agents keep working. On a sub-agent's conversation (the root's owner, at any depth) the
459
+ * `run.not_active`. On a root conversation every active sub-agent of its tree stops with its
460
+ * run, and nothing they deliver starts the root again until its next run (agent map spec §6,
461
+ * changed in acceptance 2026-11-13); `runId` is the stopped run (the latest when only sub-agents
462
+ * ran). On a sub-agent's conversation (the root's owner, at any depth) the
459
463
  * sub-agent stops with every sub-agent below it, and `runId` is the sub-agent's latest run,
460
464
  * which may already be finished when only its descendants were working (agent map spec §6).
461
465
  */
@@ -1,3 +1,4 @@
1
+ import type { ConversationSource } from "@capybara-hq/plugin-sdk";
1
2
  import { AgentLookup } from "../agents/agent-lookup.js";
2
3
  import type { ModelRef } from "../agents/definition.js";
3
4
  import type { AgentAvatar } from "../agents/profile.js";
@@ -146,6 +147,8 @@ export interface RunControl {
146
147
  * 否则返回 false。
147
148
  */
148
149
  abortIfOrphaned(conversationId: string, runId: string, byAccountId: string): Promise<boolean>;
150
+ /** Ends a run still `queued` in PG before a worker started it (`RunQueue.abortQueued`): true. */
151
+ abortQueued(conversationId: string, runId: string, byAccountId: string): Promise<boolean>;
149
152
  /**
150
153
  * 运行在等人处理(waiting)时直接中止:取消等待、写 `run_aborted`、删除 checkpoint,返回 true;
151
154
  * 运行已不是 waiting(例如刚被续跑)→ false。
@@ -228,6 +231,14 @@ export interface InternalMessageOptions {
228
231
  schedule?: ScheduleMarker;
229
232
  timeZoneOverride?: string;
230
233
  }
234
+ /** Options of a conversation created by a kernel caller (never an HTTP body). */
235
+ export interface NewConversationOptions {
236
+ /**
237
+ * Where it comes from (memory plugin spec §6.2), fixed at creation. Required: a channel or group
238
+ * conversation that fell back to `web` would be offered what only the web and DMs may see.
239
+ */
240
+ source: ConversationSource;
241
+ }
231
242
  /**
232
243
  * 会话:只有所有者可见;每次访问都实时复核 agent 对所有者仍可用(spec 3.6 R5),
233
244
  * 不可用时会话也按不存在处理(`conversation.not_found`)。
@@ -280,9 +291,10 @@ export declare class ConversationsService {
280
291
  * Creates an empty conversation bound to its device (`pick`, see
281
292
  * {@link ConversationDevices.forNew}); an agent the account cannot use →
282
293
  * `agent.not_found`, a device it may not use → `device.not_found` /
283
- * `device.unavailable`, and nothing is created.
294
+ * `device.unavailable`, and nothing is created. `opts.source` records where the conversation
295
+ * comes from.
284
296
  */
285
- create(orgId: string, accountId: string, agentId: string, pick?: NewConversationDevice): Promise<ConversationView>;
297
+ create(orgId: string, accountId: string, agentId: string, pick: NewConversationDevice | undefined, opts: NewConversationOptions): Promise<ConversationView>;
286
298
  /**
287
299
  * Creates a conversation together with its first message (UI refresh B, create on first send):
288
300
  * the conversation row and the `user_message` are written in one transaction, keyed by the
@@ -291,12 +303,12 @@ export declare class ConversationsService {
291
303
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
292
304
  * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
293
305
  * the message when the send switched), so a client can seed the new conversation's log whole.
294
- * `internal` (kernel callers only): a scheduled task's marker, which also sets
295
- * `conversations.scheduled_task_id`, and its zone override.
306
+ * `internal`: the conversation's source and, for kernel callers only, a scheduled task's marker,
307
+ * which also sets `conversations.scheduled_task_id`, and its zone override.
296
308
  */
297
309
  createWithMessage(orgId: string, accountId: string, agentId: string, input: UserMessageInput & {
298
310
  clientMessageId: string;
299
- }, pick?: NewConversationDevice, internal?: InternalMessageOptions): Promise<{
311
+ }, pick: NewConversationDevice | undefined, internal: InternalMessageOptions & NewConversationOptions): Promise<{
300
312
  conversation: ConversationView;
301
313
  event: ConversationEvent;
302
314
  events: ConversationEvent[];
@@ -525,10 +537,43 @@ export declare class ConversationsService {
525
537
  * 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
526
538
  * 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
527
539
  *
528
- * A root conversation's stop stops only its own run: its background sub-agents keep working
529
- * (agent map spec §6). A sub-agent's conversation (its owner, from the agent map) stops that
530
- * sub-agent with every descendant (`RunControl.stopTree`); `run.not_active` when nothing in that
531
- * subtree runs, else the sub-agent's latest run id.
540
+ * A root conversation's stop stops everything below it too (agent map spec §6, changed in
541
+ * acceptance 2026-11-13: after a person stops, the conversation stays stopped). In order:
542
+ * 1. `tree_stopped_at` is set on the root under its row lock: by the stopped run's terminal
543
+ * transaction (a waiting, queued or orphaned run), before the stop is asked for (a run a
544
+ * worker holds, which is active until then), or on its own (nothing of the root ran). From
545
+ * then on no sub-agent delivery wakes the root (`wakeDecisionIn`) and no
546
+ * sub-agent of the tree starts a run by a wake or a parent's message (`RunQueue.childWakeIn`),
547
+ * until the root next starts a run (a person's message, the queue, a background Bash task's
548
+ * finish), which clears it. A child that finishes just before or after the stop, `completed`
549
+ * or `aborted`, thus never wakes the root; its delivery stays in the log and reaches the
550
+ * model with the next run;
551
+ * 2. (with 1) the root's own active run stops as before ({@link abortRootRun});
552
+ * 3. every active sub-agent run of the tree stops (`RunControl.stopTree`, as the agent map's stop
553
+ * and the deletion do), with the background tasks of every sub-agent. A sub-agent the root's
554
+ * run dispatched after that read is stopped when the root run ends
555
+ * (`Waits.stopDescendantsOfStopped`).
556
+ * The root's own background Bash tasks keep running: they are the conversation's tasks, stopped
557
+ * with `TaskStop` or the task list. `run.not_active` when neither the root nor its tree had an
558
+ * active run (the stop still holds the tree's wakes); else the root's run id (its latest when only
559
+ * sub-agents ran).
560
+ *
561
+ * A sub-agent's conversation (its owner, from the agent map) stops that sub-agent with every
562
+ * descendant (`RunControl.stopTree`); `run.not_active` when nothing in that subtree runs, else
563
+ * the sub-agent's latest run id. Its `aborted` finish never wakes its parent.
532
564
  */
533
565
  abort(orgId: string, conversationId: string, accountId: string): Promise<string>;
566
+ /**
567
+ * Marks a person's stop of the root's tree (`tree_stopped_at`, see {@link abort}); committed
568
+ * before any stop, so every wake decided after it (under the root's row lock) sees it.
569
+ */
570
+ private holdTree;
571
+ /**
572
+ * Stops a root conversation's own active run (queued / running / waiting) and returns its id;
573
+ * null when none is active. Its sub-agents are the caller's. The tree's hold
574
+ * (`tree_stopped_at`) is written by the stopped run's terminal transaction (`RunFinish.finishIn`
575
+ * for a run a person stopped), in the lock order every stop and decision takes (conversation,
576
+ * decided row, run); a run a worker holds is marked before its stop is asked for as well.
577
+ */
578
+ private abortRootRun;
534
579
  }
@@ -177,9 +177,10 @@ let ConversationsService = class ConversationsService {
177
177
  * Creates an empty conversation bound to its device (`pick`, see
178
178
  * {@link ConversationDevices.forNew}); an agent the account cannot use →
179
179
  * `agent.not_found`, a device it may not use → `device.not_found` /
180
- * `device.unavailable`, and nothing is created.
180
+ * `device.unavailable`, and nothing is created. `opts.source` records where the conversation
181
+ * comes from.
181
182
  */
182
- async create(orgId, accountId, agentId, pick = {}) {
183
+ async create(orgId, accountId, agentId, pick = {}, opts) {
183
184
  await this.agents.get(orgId, agentId, accountId);
184
185
  const deviceId = await this.devices.forNew(orgId, accountId, pick);
185
186
  const row = await this.db
@@ -190,6 +191,7 @@ let ConversationsService = class ConversationsService {
190
191
  agent_id: agentId,
191
192
  account_id: accountId,
192
193
  device_id: deviceId,
194
+ source: opts.source,
193
195
  })
194
196
  .returning(COLUMNS)
195
197
  .executeTakeFirstOrThrow();
@@ -203,10 +205,10 @@ let ConversationsService = class ConversationsService {
203
205
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
204
206
  * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
205
207
  * the message when the send switched), so a client can seed the new conversation's log whole.
206
- * `internal` (kernel callers only): a scheduled task's marker, which also sets
207
- * `conversations.scheduled_task_id`, and its zone override.
208
+ * `internal`: the conversation's source and, for kernel callers only, a scheduled task's marker,
209
+ * which also sets `conversations.scheduled_task_id`, and its zone override.
208
210
  */
209
- async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal = {}) {
211
+ async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal) {
210
212
  const agent = await this.agents.get(orgId, agentId, accountId);
211
213
  const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
212
214
  if (prior)
@@ -242,6 +244,7 @@ let ConversationsService = class ConversationsService {
242
244
  device_id: conv.deviceId,
243
245
  create_request_id: input.clientMessageId,
244
246
  scheduled_task_id: internal.schedule?.taskId ?? null,
247
+ source: internal.source,
245
248
  })
246
249
  .onConflict((oc) => oc
247
250
  .columns(["org_id", "account_id", "create_request_id"])
@@ -339,13 +342,11 @@ let ConversationsService = class ConversationsService {
339
342
  * log, runs and trajectory stay (super admins still see them).
340
343
  */
341
344
  async remove(orgId, conversationId, accountId) {
342
- await this.writable(orgId, conversationId, accountId);
343
- try {
344
- await this.abort(orgId, conversationId, accountId);
345
- }
346
- catch (e) {
347
- if (!(e instanceof ApiError && e.code === "run.not_active"))
348
- throw e;
345
+ const conv = await this.writable(orgId, conversationId, accountId);
346
+ // the root's own run only: the tree stops below, after the mark (a sub-agent's conversation
347
+ // is read-only, so `writable` refused it)
348
+ if (conv.parentConversationId === null) {
349
+ await this.abortRootRun(conversationId, accountId);
349
350
  }
350
351
  await this.markDeleted(orgId, conversationId, accountId);
351
352
  // after the mark, so no finish or delivery of the stops below can start a run on the deleted
@@ -978,25 +979,77 @@ let ConversationsService = class ConversationsService {
978
979
  * 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
979
980
  * 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
980
981
  *
981
- * A root conversation's stop stops only its own run: its background sub-agents keep working
982
- * (agent map spec §6). A sub-agent's conversation (its owner, from the agent map) stops that
983
- * sub-agent with every descendant (`RunControl.stopTree`); `run.not_active` when nothing in that
984
- * subtree runs, else the sub-agent's latest run id.
982
+ * A root conversation's stop stops everything below it too (agent map spec §6, changed in
983
+ * acceptance 2026-11-13: after a person stops, the conversation stays stopped). In order:
984
+ * 1. `tree_stopped_at` is set on the root under its row lock: by the stopped run's terminal
985
+ * transaction (a waiting, queued or orphaned run), before the stop is asked for (a run a
986
+ * worker holds, which is active until then), or on its own (nothing of the root ran). From
987
+ * then on no sub-agent delivery wakes the root (`wakeDecisionIn`) and no
988
+ * sub-agent of the tree starts a run by a wake or a parent's message (`RunQueue.childWakeIn`),
989
+ * until the root next starts a run (a person's message, the queue, a background Bash task's
990
+ * finish), which clears it. A child that finishes just before or after the stop, `completed`
991
+ * or `aborted`, thus never wakes the root; its delivery stays in the log and reaches the
992
+ * model with the next run;
993
+ * 2. (with 1) the root's own active run stops as before ({@link abortRootRun});
994
+ * 3. every active sub-agent run of the tree stops (`RunControl.stopTree`, as the agent map's stop
995
+ * and the deletion do), with the background tasks of every sub-agent. A sub-agent the root's
996
+ * run dispatched after that read is stopped when the root run ends
997
+ * (`Waits.stopDescendantsOfStopped`).
998
+ * The root's own background Bash tasks keep running: they are the conversation's tasks, stopped
999
+ * with `TaskStop` or the task list. `run.not_active` when neither the root nor its tree had an
1000
+ * active run (the stop still holds the tree's wakes); else the root's run id (its latest when only
1001
+ * sub-agents ran).
1002
+ *
1003
+ * A sub-agent's conversation (its owner, from the agent map) stops that sub-agent with every
1004
+ * descendant (`RunControl.stopTree`); `run.not_active` when nothing in that subtree runs, else
1005
+ * the sub-agent's latest run id. Its `aborted` finish never wakes its parent.
985
1006
  */
986
1007
  async abort(orgId, conversationId, accountId) {
987
1008
  const conv = await this.get(orgId, conversationId, accountId);
1009
+ const latest = () => this.db
1010
+ .selectFrom("runs")
1011
+ .select("id")
1012
+ .where("conversation_id", "=", conversationId)
1013
+ .orderBy("created_at", "desc")
1014
+ .executeTakeFirst();
988
1015
  if (conv.parentConversationId !== null) {
989
- const latest = await this.db
990
- .selectFrom("runs")
991
- .select("id")
992
- .where("conversation_id", "=", conversationId)
993
- .orderBy("created_at", "desc")
994
- .executeTakeFirst();
1016
+ const last = await latest();
995
1017
  const n = await this.runs.stopTree(conversationId, accountId);
996
- if (n === 0 || !latest)
1018
+ if (n === 0 || !last)
997
1019
  throw new ApiError("run.not_active", "No active run to abort");
998
- return latest.id;
1020
+ return last.id;
999
1021
  }
1022
+ const stopped = await this.abortRootRun(conversationId, accountId);
1023
+ // nothing of the root's ran: the hold is the stop's own write
1024
+ if (stopped === null)
1025
+ await this.holdTree(conversationId);
1026
+ const n = await this.runs.stopTree(conversationId, accountId, { includeSelf: false });
1027
+ if (stopped !== null)
1028
+ return stopped;
1029
+ const last = await latest();
1030
+ if (n === 0 || !last)
1031
+ throw new ApiError("run.not_active", "No active run to abort");
1032
+ return last.id;
1033
+ }
1034
+ /**
1035
+ * Marks a person's stop of the root's tree (`tree_stopped_at`, see {@link abort}); committed
1036
+ * before any stop, so every wake decided after it (under the root's row lock) sees it.
1037
+ */
1038
+ async holdTree(conversationId) {
1039
+ await this.db
1040
+ .updateTable("conversations")
1041
+ .set({ tree_stopped_at: sql `now()` })
1042
+ .where("id", "=", conversationId)
1043
+ .execute();
1044
+ }
1045
+ /**
1046
+ * Stops a root conversation's own active run (queued / running / waiting) and returns its id;
1047
+ * null when none is active. Its sub-agents are the caller's. The tree's hold
1048
+ * (`tree_stopped_at`) is written by the stopped run's terminal transaction (`RunFinish.finishIn`
1049
+ * for a run a person stopped), in the lock order every stop and decision takes (conversation,
1050
+ * decided row, run); a run a worker holds is marked before its stop is asked for as well.
1051
+ */
1052
+ async abortRootRun(conversationId, accountId) {
1000
1053
  const run = await this.db
1001
1054
  .selectFrom("runs")
1002
1055
  .select(["id", "status"])
@@ -1005,7 +1058,7 @@ let ConversationsService = class ConversationsService {
1005
1058
  .orderBy("created_at", "desc")
1006
1059
  .executeTakeFirst();
1007
1060
  if (!run)
1008
- throw new ApiError("run.not_active", "No active run to abort");
1061
+ return null;
1009
1062
  const runs = this.runs;
1010
1063
  // 等待中:直接结束;没成功(刚被续跑、已回到 queued)则按下面的常规路径中止。
1011
1064
  // the stop's terminal transaction owed the ensure-run backstop; the direct call starts what
@@ -1014,11 +1067,24 @@ let ConversationsService = class ConversationsService {
1014
1067
  await runs.ensureRunAfterCommit(conversationId);
1015
1068
  return run.id;
1016
1069
  }
1070
+ // a queued run no worker has started yet is ended in PG (ruling R27), never left to the stop
1071
+ // marker's TTL: the marker first, so a worker that takes it meanwhile stops too
1072
+ if (run.status === "queued") {
1073
+ await runs.requestAbort(run.id, accountId);
1074
+ if (await runs.abortQueued(conversationId, run.id, accountId)) {
1075
+ await runs.ensureRunAfterCommit(conversationId);
1076
+ }
1077
+ return run.id;
1078
+ }
1017
1079
  // 孤儿运行没有 worker 在听中止广播:直接写成 aborted,之后再看有没有等待中的消息要起新一轮。
1018
1080
  if (await runs.abortIfOrphaned(conversationId, run.id, accountId)) {
1019
1081
  await runs.ensureRunAfterCommit(conversationId);
1020
1082
  return run.id;
1021
1083
  }
1084
+ // a run a worker holds ends later: the hold is written now, before the stop is asked for,
1085
+ // while the run is still active (no wake can start the root before it), and again by its
1086
+ // terminal transaction
1087
+ await this.holdTree(conversationId);
1022
1088
  await runs.requestAbort(run.id, accountId);
1023
1089
  return run.id;
1024
1090
  }
@@ -1041,6 +1107,9 @@ __decorate([
1041
1107
  __decorate([
1042
1108
  Transactional({ propagation: "mandatory" })
1043
1109
  ], ConversationsService.prototype, "bindIn", null);
1110
+ __decorate([
1111
+ Transactional()
1112
+ ], ConversationsService.prototype, "holdTree", null);
1044
1113
  ConversationsService = __decorate([
1045
1114
  Injectable(),
1046
1115
  __param(0, Inject(KernelDb)),
@@ -200,6 +200,20 @@ export declare const RunContextSchema: z.ZodObject<{
200
200
  person: z.ZodString;
201
201
  }, z.core.$strip>>;
202
202
  }, z.core.$strip>;
203
+ /**
204
+ * The most UTF-8 bytes a `plugin_context` event's text may hold (memory spec §6.1): the providers'
205
+ * limit, so the log takes every text a provider may return.
206
+ */
207
+ export declare const PLUGIN_CONTEXT_MAX_BYTES: number;
208
+ /**
209
+ * `plugin_context` (memory spec §6.1): the text a `context@1` provider added at a run start. Empty
210
+ * text means the provider has nothing (any more); `hash` is the sha256 (hex) of `text`.
211
+ */
212
+ export declare const PluginContextSchema: z.ZodObject<{
213
+ plugin: z.ZodString;
214
+ text: z.ZodString;
215
+ hash: z.ZodString;
216
+ }, z.core.$strip>;
203
217
  /**
204
218
  * 会话事件(`type` + `payload`)的判别联合。事件日志只追加,写入前用它校验 payload;
205
219
  * 读出时按写入时的形状原样返回。
@@ -547,6 +561,13 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
547
561
  person: z.ZodString;
548
562
  }, z.core.$strip>>;
549
563
  }, z.core.$strip>;
564
+ }, z.core.$strip>, z.ZodObject<{
565
+ type: z.ZodLiteral<"plugin_context">;
566
+ payload: z.ZodObject<{
567
+ plugin: z.ZodString;
568
+ text: z.ZodString;
569
+ hash: z.ZodString;
570
+ }, z.core.$strip>;
550
571
  }, z.core.$strip>, z.ZodObject<{
551
572
  type: z.ZodLiteral<"tools_changed">;
552
573
  payload: z.ZodObject<{
@@ -716,6 +737,12 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
716
737
  byAccountId: z.ZodNullable<z.ZodString>;
717
738
  reason: z.ZodOptional<z.ZodString>;
718
739
  }, z.core.$strip>;
740
+ }, z.core.$strip>, z.ZodObject<{
741
+ type: z.ZodLiteral<"approval_updated">;
742
+ payload: z.ZodObject<{
743
+ approvalId: z.ZodString;
744
+ callId: z.ZodString;
745
+ }, z.core.$strip>;
719
746
  }, z.core.$strip>, z.ZodObject<{
720
747
  type: z.ZodLiteral<"question_asked">;
721
748
  payload: z.ZodObject<{