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

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 (247) hide show
  1. package/bootstrap.json +10 -1
  2. package/dist/agents/admin-agents.d.ts +16 -3
  3. package/dist/agents/admin-agents.js +65 -7
  4. package/dist/agents/agents.schemas.d.ts +24 -6
  5. package/dist/agents/agents.service.d.ts +4 -3
  6. package/dist/agents/agents.service.js +39 -26
  7. package/dist/agents/definition.d.ts +44 -6
  8. package/dist/agents/definition.js +86 -18
  9. package/dist/agents/from-template.d.ts +6 -3
  10. package/dist/agents/from-template.js +57 -44
  11. package/dist/agents/org-templates.js +97 -41
  12. package/dist/agents/templates.d.ts +6 -0
  13. package/dist/agents/templates.js +15 -1
  14. package/dist/catalog/admin-catalog.controller.d.ts +2 -4
  15. package/dist/catalog/admin-catalog.controller.js +2 -6
  16. package/dist/catalog/catalog.module.js +3 -0
  17. package/dist/catalog/claude/report.d.ts +9 -2
  18. package/dist/catalog/claude/report.js +1 -0
  19. package/dist/catalog/claude/translate.js +1 -1
  20. package/dist/catalog/component-promotion.js +1 -2
  21. package/dist/catalog/component-state.d.ts +13 -1
  22. package/dist/catalog/component-state.js +14 -5
  23. package/dist/catalog/marketplaces.service.d.ts +1 -1
  24. package/dist/catalog/marketplaces.service.js +2 -1
  25. package/dist/catalog/org-catalog.controller.d.ts +13 -3
  26. package/dist/catalog/org-catalog.controller.js +26 -4
  27. package/dist/catalog/org-catalog.schemas.d.ts +13 -0
  28. package/dist/catalog/org-catalog.schemas.js +34 -0
  29. package/dist/catalog/org-plugin-uploads.controller.js +0 -1
  30. package/dist/catalog/org-plugin-uploads.d.ts +0 -1
  31. package/dist/catalog/org-plugin-uploads.js +0 -3
  32. package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
  33. package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
  34. package/dist/catalog/platform-components.d.ts +0 -1
  35. package/dist/catalog/platform-components.js +3 -8
  36. package/dist/catalog/plugin-import.d.ts +0 -8
  37. package/dist/catalog/plugin-import.js +4 -35
  38. package/dist/catalog/plugin-install.d.ts +2 -2
  39. package/dist/catalog/plugin-install.js +2 -2
  40. package/dist/catalog/plugin-package.d.ts +7 -0
  41. package/dist/catalog/plugin-package.js +27 -1
  42. package/dist/catalog/plugin-upgrade.d.ts +5 -6
  43. package/dist/catalog/plugin-upgrade.js +5 -23
  44. package/dist/catalog/requests.service.d.ts +66 -10
  45. package/dist/catalog/requests.service.js +196 -15
  46. package/dist/channels/actions.d.ts +2 -0
  47. package/dist/channels/actions.js +19 -4
  48. package/dist/channels/cards.d.ts +16 -2
  49. package/dist/channels/cards.js +40 -7
  50. package/dist/channels/channels.module.js +3 -0
  51. package/dist/channels/outbound.d.ts +22 -1
  52. package/dist/channels/outbound.js +116 -7
  53. package/dist/channels/phrases.d.ts +25 -0
  54. package/dist/channels/phrases.js +19 -0
  55. package/dist/channels/threads.d.ts +12 -0
  56. package/dist/channels/threads.js +34 -0
  57. package/dist/config/schema.d.ts +23 -1
  58. package/dist/config/schema.js +105 -5
  59. package/dist/conversations/conversations.service.d.ts +42 -9
  60. package/dist/conversations/conversations.service.js +57 -25
  61. package/dist/conversations/events.d.ts +106 -11
  62. package/dist/conversations/events.js +121 -18
  63. package/dist/conversations/message-context.d.ts +6 -3
  64. package/dist/conversations/message-context.js +14 -10
  65. package/dist/conversations/queue.d.ts +15 -2
  66. package/dist/conversations/queue.js +22 -3
  67. package/dist/conversations/queue.service.d.ts +12 -6
  68. package/dist/conversations/queue.service.js +36 -13
  69. package/dist/db/kernel-schema.d.ts +105 -3
  70. package/dist/devices/conversation-devices.d.ts +8 -1
  71. package/dist/devices/conversation-devices.js +8 -0
  72. package/dist/devices/devices.controller.d.ts +21 -4
  73. package/dist/devices/devices.controller.js +46 -8
  74. package/dist/devices/devices.schemas.d.ts +9 -1
  75. package/dist/devices/devices.schemas.js +11 -3
  76. package/dist/devices/devices.service.d.ts +176 -4
  77. package/dist/devices/devices.service.js +341 -6
  78. package/dist/devices/dto.d.ts +56 -3
  79. package/dist/devices/dto.js +51 -1
  80. package/dist/devices/public-suffix.d.ts +6 -0
  81. package/dist/devices/public-suffix.js +79 -0
  82. package/dist/errors.d.ts +46 -5
  83. package/dist/errors.js +46 -5
  84. package/dist/evals/reconstruct.d.ts +18 -13
  85. package/dist/evals/reconstruct.js +50 -36
  86. package/dist/evals/recorded-tools.d.ts +7 -7
  87. package/dist/evals/recorded-tools.js +4 -3
  88. package/dist/evals/trajectory.d.ts +6 -1
  89. package/dist/evals/trajectory.js +7 -0
  90. package/dist/extensions/bootstrap.d.ts +49 -10
  91. package/dist/extensions/bootstrap.js +104 -27
  92. package/dist/extensions/package.js +2 -2
  93. package/dist/http/api.module.js +5 -0
  94. package/dist/inbox/inbox.module.js +2 -1
  95. package/dist/inbox/inbox.service.d.ts +19 -4
  96. package/dist/inbox/inbox.service.js +14 -3
  97. package/dist/infra/config/kernel-config.service.js +11 -0
  98. package/dist/infra/events/kernel-events.d.ts +2 -1
  99. package/dist/lifecycle/kernel-lifecycle.d.ts +5 -1
  100. package/dist/lifecycle/kernel-lifecycle.js +16 -4
  101. package/dist/local-devices/device-policy.service.d.ts +65 -0
  102. package/dist/local-devices/device-policy.service.js +217 -0
  103. package/dist/local-devices/local-device-gateway.js +4 -1
  104. package/dist/local-devices/local-device-provider.d.ts +40 -3
  105. package/dist/local-devices/local-device-provider.js +109 -7
  106. package/dist/local-devices/local-devices.module.d.ts +2 -1
  107. package/dist/local-devices/local-devices.module.js +19 -3
  108. package/dist/local-devices/machines.d.ts +14 -3
  109. package/dist/local-devices/machines.js +41 -3
  110. package/dist/local-devices/network-grants.d.ts +104 -0
  111. package/dist/local-devices/network-grants.js +288 -0
  112. package/dist/local-devices/policy-summary.d.ts +20 -0
  113. package/dist/local-devices/policy-summary.js +63 -0
  114. package/dist/orgs/orgs.module.js +4 -10
  115. package/dist/orgs/roles.d.ts +1 -1
  116. package/dist/orgs/roles.js +4 -0
  117. package/dist/orgs/settings.controller.js +2 -1
  118. package/dist/orgs/settings.service.d.ts +164 -12
  119. package/dist/orgs/settings.service.js +212 -24
  120. package/dist/platform/exclusions.js +11 -1
  121. package/dist/platform/operation.js +4 -0
  122. package/dist/platform/ops/admin-items.ops.js +1 -1
  123. package/dist/platform/ops/agent-skills.ops.js +2 -2
  124. package/dist/platform/ops/agent-templates.ops.js +2 -2
  125. package/dist/platform/ops/agents.ops.js +11 -15
  126. package/dist/platform/ops/devices.ops.js +3 -1
  127. package/dist/platform/ops/inbox.ops.js +19 -0
  128. package/dist/platform/ops/org-settings.ops.js +14 -5
  129. package/dist/platform/ops/platform.ops.d.ts +9 -1
  130. package/dist/platform/ops/platform.ops.js +34 -12
  131. package/dist/platform/ops/requests.ops.js +3 -2
  132. package/dist/platform/ops/schedules.ops.d.ts +21 -0
  133. package/dist/platform/ops/schedules.ops.js +470 -0
  134. package/dist/platform/ops/skills.ops.js +3 -3
  135. package/dist/platform/platform-ops.module.d.ts +2 -1
  136. package/dist/platform/platform-ops.module.js +5 -0
  137. package/dist/platform/route-schemas.js +3 -0
  138. package/dist/roles/worker.module.js +3 -0
  139. package/dist/runtime/context/build.js +13 -4
  140. package/dist/runtime/context/compaction.js +6 -3
  141. package/dist/runtime/context/device-paths.d.ts +43 -21
  142. package/dist/runtime/context/device-paths.js +104 -21
  143. package/dist/runtime/context/run-context.d.ts +9 -0
  144. package/dist/runtime/context/run-context.js +8 -0
  145. package/dist/runtime/context/skills.d.ts +14 -1
  146. package/dist/runtime/context/skills.js +36 -5
  147. package/dist/runtime/maintenance.d.ts +4 -2
  148. package/dist/runtime/maintenance.js +16 -5
  149. package/dist/runtime/run-finish.d.ts +6 -1
  150. package/dist/runtime/run-finish.js +16 -2
  151. package/dist/runtime/run-queue.js +21 -2
  152. package/dist/runtime/run-worker.js +14 -33
  153. package/dist/runtime/runtime.module.js +3 -0
  154. package/dist/runtime/subagents/definition.d.ts +32 -14
  155. package/dist/runtime/subagents/definition.js +106 -109
  156. package/dist/runtime/subagents/dispatch.js +4 -3
  157. package/dist/runtime/subagents/preamble.d.ts +2 -1
  158. package/dist/runtime/subagents/preamble.js +2 -1
  159. package/dist/runtime/waits.js +14 -4
  160. package/dist/sandbox/sandbox.service.d.ts +120 -10
  161. package/dist/sandbox/sandbox.service.js +335 -28
  162. package/dist/sandbox/tiers.d.ts +86 -0
  163. package/dist/sandbox/tiers.js +96 -0
  164. package/dist/schedules/effective-settings.d.ts +16 -0
  165. package/dist/schedules/effective-settings.js +17 -0
  166. package/dist/schedules/errors.d.ts +8 -0
  167. package/dist/schedules/errors.js +8 -0
  168. package/dist/schedules/fire.d.ts +118 -0
  169. package/dist/schedules/fire.js +625 -0
  170. package/dist/schedules/grant-rules.d.ts +94 -0
  171. package/dist/schedules/grant-rules.js +126 -0
  172. package/dist/schedules/job-ids.d.ts +32 -0
  173. package/dist/schedules/job-ids.js +27 -0
  174. package/dist/schedules/pause.d.ts +9 -0
  175. package/dist/schedules/pause.js +33 -0
  176. package/dist/schedules/run-tracker.d.ts +81 -0
  177. package/dist/schedules/run-tracker.js +333 -0
  178. package/dist/schedules/scan.job.d.ts +64 -0
  179. package/dist/schedules/scan.job.js +303 -0
  180. package/dist/schedules/schedule-math.d.ts +31 -0
  181. package/dist/schedules/schedule-math.js +223 -0
  182. package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
  183. package/dist/schedules/scheduled-tasks.service.js +1193 -0
  184. package/dist/schedules/schedules.controller.d.ts +68 -0
  185. package/dist/schedules/schedules.controller.js +242 -0
  186. package/dist/schedules/schedules.module.d.ts +16 -0
  187. package/dist/schedules/schedules.module.js +65 -0
  188. package/dist/schedules/schedules.schemas.d.ts +316 -0
  189. package/dist/schedules/schedules.schemas.js +186 -0
  190. package/dist/schedules/store.d.ts +81 -0
  191. package/dist/schedules/store.js +390 -0
  192. package/dist/schedules/store.module.d.ts +6 -0
  193. package/dist/schedules/store.module.js +21 -0
  194. package/dist/schedules/types.d.ts +108 -0
  195. package/dist/schedules/types.js +1 -0
  196. package/dist/schedules/unattended.d.ts +117 -0
  197. package/dist/schedules/unattended.js +228 -0
  198. package/dist/setup/oauth-token-probe.js +8 -4
  199. package/dist/skills/agent-skills.service.d.ts +4 -8
  200. package/dist/skills/agent-skills.service.js +18 -53
  201. package/dist/skills/catalog.d.ts +65 -62
  202. package/dist/skills/catalog.js +109 -206
  203. package/dist/skills/conversation-catalog.d.ts +3 -3
  204. package/dist/skills/conversation-catalog.js +5 -12
  205. package/dist/skills/invoke.d.ts +2 -2
  206. package/dist/skills/skills.controller.d.ts +3 -1
  207. package/dist/skills/skills.service.d.ts +38 -37
  208. package/dist/skills/skills.service.js +168 -107
  209. package/dist/skills/usable.d.ts +57 -8
  210. package/dist/skills/usable.js +172 -38
  211. package/dist/tools/approvals.controller.d.ts +4 -2
  212. package/dist/tools/approvals.controller.js +17 -2
  213. package/dist/tools/approvals.schemas.d.ts +1 -0
  214. package/dist/tools/approvals.schemas.js +3 -1
  215. package/dist/tools/approvals.service.d.ts +182 -7
  216. package/dist/tools/approvals.service.js +597 -13
  217. package/dist/tools/builtin-names.d.ts +25 -4
  218. package/dist/tools/builtin-names.js +45 -4
  219. package/dist/tools/builtins/builtin-tools.d.ts +8 -0
  220. package/dist/tools/builtins/builtin-tools.js +23 -2
  221. package/dist/tools/builtins/sandbox/bash.js +16 -7
  222. package/dist/tools/builtins/sandbox/common.d.ts +1 -1
  223. package/dist/tools/builtins/sandbox/common.js +33 -10
  224. package/dist/tools/builtins/sandbox/edit.js +7 -3
  225. package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
  226. package/dist/tools/builtins/sandbox/file-state.js +33 -3
  227. package/dist/tools/builtins/sandbox/write.js +8 -4
  228. package/dist/tools/builtins/schedules.d.ts +107 -0
  229. package/dist/tools/builtins/schedules.js +789 -0
  230. package/dist/tools/elicitation.d.ts +2 -0
  231. package/dist/tools/elicitation.js +1 -1
  232. package/dist/tools/fixed-ask.d.ts +7 -0
  233. package/dist/tools/fixed-ask.js +9 -0
  234. package/dist/tools/network-asks.d.ts +109 -0
  235. package/dist/tools/network-asks.js +221 -0
  236. package/dist/tools/pipeline.d.ts +53 -6
  237. package/dist/tools/pipeline.js +202 -13
  238. package/dist/tools/policy.d.ts +21 -8
  239. package/dist/tools/policy.js +63 -9
  240. package/dist/tools/registry.d.ts +10 -1
  241. package/dist/tools/tools.module.js +12 -0
  242. package/package.json +2 -2
  243. package/src/migrations/0113_scheduled_tasks.sql +108 -0
  244. package/src/migrations/0114_network_asks.sql +23 -0
  245. package/src/migrations/0115_network_requests.sql +28 -0
  246. package/src/migrations/0116_skills_default_on.sql +37 -0
  247. package/src/migrations/0117_sandbox_tiers.sql +11 -0
@@ -1,5 +1,5 @@
1
1
  import { isIP } from "node:net";
2
- import { DOCUMENT_MEDIA_TYPES, IMAGE_MEDIA_TYPES, validateAllowHost, } from "@capybara-hq/plugin-sdk";
2
+ import { DEV_HOSTS, DOCUMENT_MEDIA_TYPES, IMAGE_MEDIA_TYPES, tierAtMost, validateAllowHost, } from "@capybara-hq/plugin-sdk";
3
3
  import { z } from "zod";
4
4
  import { LOG_FORMATS, LOG_LEVELS } from "../log.js";
5
5
  import { noProxyEntryProblem, proxyUrlProblem } from "../net/proxy.js";
@@ -965,10 +965,24 @@ export const KernelConfigSchema = z.object({
965
965
  .int()
966
966
  .min(0)
967
967
  .refine((n) => n === 0 || n >= 10_000, "0 (no limit) or at least 10,000")
968
- .default(5_000_000)
968
+ .default(0)
969
969
  .meta(meta({
970
970
  hot: true,
971
- description: "Tokens (input, output and cache) all sub-agents of the tree under a person's message may use together; at the cap running sub-agents stop and new dispatches are refused. 0 means no limit; otherwise at least 10,000. Organizations can be given their own value in subagentLimitOverrides",
971
+ description: "Tokens (input, output and cache) all sub-agents of the tree under a person's message may use together; at the cap running sub-agents stop and new dispatches are refused. 0 (the default) means no limit; otherwise at least 10,000. Organizations can be given their own value in subagentLimitOverrides",
972
+ })),
973
+ })
974
+ .prefault({}),
975
+ skills: z
976
+ .object({
977
+ catalogBudgetChars: z
978
+ .number()
979
+ .int()
980
+ .min(1_000)
981
+ .max(1_000_000)
982
+ .default(16_000)
983
+ .meta(meta({
984
+ hot: true,
985
+ description: "Characters the skills catalog lines with descriptions may take in a conversation's skills snapshot (about 4 characters per token). At run start the skills are ranked (the agent's own and the ones it shared, builtin, the organization's own, then plugin skills; by name within each) and keep their descriptions while the running total fits; the rest are listed by name only. Recorded in the snapshot, so a change applies from the next snapshot a run start writes",
972
986
  })),
973
987
  })
974
988
  .prefault({}),
@@ -1030,6 +1044,46 @@ export const KernelConfigSchema = z.object({
1030
1044
  .prefault({}),
1031
1045
  })
1032
1046
  .prefault({}),
1047
+ schedules: z
1048
+ .object({
1049
+ minIntervalFloorMinutes: z
1050
+ .number()
1051
+ .int()
1052
+ .positive()
1053
+ .default(5)
1054
+ .meta(meta({
1055
+ hot: true,
1056
+ description: "Smallest value an organization may set for the minimum gap between two fire times of a scheduled task, in minutes; a lower org value is clamped up to it",
1057
+ })),
1058
+ maxTasksPerMemberCap: z
1059
+ .number()
1060
+ .int()
1061
+ .positive()
1062
+ .default(100)
1063
+ .meta(meta({
1064
+ hot: true,
1065
+ description: "Largest number of scheduled tasks per member an organization may allow; a higher org value is clamped down to it",
1066
+ })),
1067
+ maxTasksPerOrgCap: z
1068
+ .number()
1069
+ .int()
1070
+ .positive()
1071
+ .default(2000)
1072
+ .meta(meta({
1073
+ hot: true,
1074
+ description: "Largest number of scheduled tasks per organization an organization may allow; a higher org value is clamped down to it",
1075
+ })),
1076
+ maxRunsPerHourCap: z
1077
+ .number()
1078
+ .int()
1079
+ .positive()
1080
+ .default(600)
1081
+ .meta(meta({
1082
+ hot: true,
1083
+ description: "Largest number of scheduled task runs per organization per hour an organization may allow; a higher org value is clamped down to it",
1084
+ })),
1085
+ })
1086
+ .prefault({}),
1033
1087
  sandbox: z
1034
1088
  .object({
1035
1089
  image: z
@@ -1151,10 +1205,14 @@ export const KernelConfigSchema = z.object({
1151
1205
  description: "Minutes a device's instance may run continuously before it is suspended even while in use (skipped while a command is running)",
1152
1206
  })),
1153
1207
  egress: z
1154
- .enum(["none", "allowlist", "open"])
1208
+ .enum(["none", "allowlist"], {
1209
+ error: (iss) => iss.input === "open"
1210
+ ? 'sandbox.limits.egress no longer takes "open": open egress is the open sandbox tier now; set sandbox.tiers.max to "open" (and egress to "allowlist")'
1211
+ : undefined,
1212
+ })
1155
1213
  .default("allowlist")
1156
1214
  .meta(meta({
1157
- description: "Outbound network from cloud device instances: none, allowlist (only allowHosts) or open; applies to instances created after the change, existing ones keep theirs until destroyed",
1215
+ description: 'Outbound network of sandboxed devices: none (no tier reaches the network, the kill switch) or allowlist (strict reaches allowHosts, dev adds devHosts, open reaches any public host). Open egress is the open tier: set sandbox.tiers.max to "open"',
1158
1216
  })),
1159
1217
  allowHosts: z
1160
1218
  .array(z.string())
@@ -1169,6 +1227,48 @@ export const KernelConfigSchema = z.object({
1169
1227
  .meta(meta({
1170
1228
  description: "Hosts cloud device instances may reach when egress is allowlist: lowercase hostnames, optional leading '*.', no IPs, ports or internal-only names; applies to instances created after the change",
1171
1229
  })),
1230
+ devHosts: z
1231
+ .array(z.string())
1232
+ .default([...DEV_HOSTS])
1233
+ .superRefine((hosts, ctx) => {
1234
+ hosts.forEach((h, i) => {
1235
+ for (const message of validateAllowHost(h)) {
1236
+ ctx.addIssue({ code: "custom", path: [i], message: `${h}: ${message}` });
1237
+ }
1238
+ });
1239
+ })
1240
+ .meta(meta({
1241
+ description: "Hosts the dev sandbox tier adds to the allowlist (package registries, source hosts, tool downloads) when egress is allowlist; same format as allowHosts",
1242
+ })),
1243
+ })
1244
+ .prefault({}),
1245
+ tiers: z
1246
+ .object({
1247
+ max: z
1248
+ .enum(["strict", "dev", "open"])
1249
+ .default("dev")
1250
+ .meta(meta({
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",
1253
+ })),
1254
+ default: z
1255
+ .enum(["strict", "dev", "open"])
1256
+ .default("dev")
1257
+ .meta(meta({
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",
1260
+ })),
1261
+ allowUnsandboxed: z
1262
+ .boolean()
1263
+ .default(true)
1264
+ .meta(meta({
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",
1267
+ })),
1268
+ })
1269
+ .refine((t) => tierAtMost(t.default, t.max), {
1270
+ message: "sandbox.tiers.default must not be above sandbox.tiers.max",
1271
+ path: ["default"],
1172
1272
  })
1173
1273
  .prefault({}),
1174
1274
  })
@@ -9,7 +9,9 @@ import { TransactionHost } from "../infra/database/transaction-host.js";
9
9
  import { KernelEvents } from "../infra/events/kernel-events.js";
10
10
  import type { KernelLogger } from "../log.js";
11
11
  import { ProviderRegistry } from "../runtime/providers.js";
12
+ import type { ScheduleMarker } from "../schedules/types.js";
12
13
  import { type RenderedInvocation, SkillInvoker } from "../skills/invoke.js";
14
+ import { type ApprovalRecord, ApprovalsService } from "../tools/approvals.service.js";
13
15
  import { ConnectorContent } from "../tools/connector-content.js";
14
16
  import { type QuestionRecord, QuestionsService } from "../tools/questions.service.js";
15
17
  import { ConversationAccess } from "./access.js";
@@ -46,6 +48,8 @@ export interface ConversationView {
46
48
  * sub-agent's is null: it works in its root conversation's.
47
49
  */
48
50
  deviceId: string | null;
51
+ /** The scheduled task that created it (a `new`-mode task's run); null otherwise (spec §4). */
52
+ scheduledTaskId: string | null;
49
53
  /**
50
54
  * What the latest run is doing: `running` (queued or running), `waiting` (an approval or a
51
55
  * question), else null. Only the list queries compute it; other responses carry null.
@@ -101,6 +105,17 @@ export interface SubagentView {
101
105
  pendingApprovalIds: string[];
102
106
  pendingQuestionIds: string[];
103
107
  }
108
+ /**
109
+ * What a message skipped (reply-while-question spec §8.1, network ask-to-allow spec §6.7): its
110
+ * author's pending questions and network asks, and the highest seq the network skips wrote per
111
+ * conversation (a sub-agent's ask is in its own conversation).
112
+ */
113
+ export interface ReplySkips {
114
+ questions: QuestionRecord[];
115
+ network: ApprovalRecord[];
116
+ seqs: ReadonlyMap<string, number>;
117
+ }
118
+ export declare const NO_REPLY_SKIPS: ReplySkips;
104
119
  /** 会话服务用到的运行控制:投递运行(RunQueue)与跨副本中止(RunAbort)。 */
105
120
  export interface RunControl {
106
121
  /** 有未消费的消息且没有进行中的运行时投递一轮运行;返回 runId 或 null。 */
@@ -185,6 +200,15 @@ export interface UserMessageInput {
185
200
  */
186
201
  im?: ImSenderContext;
187
202
  }
203
+ /**
204
+ * Message options only kernel callers pass (a scheduled task's fire, scheduled tasks spec §6), never
205
+ * an HTTP body: the schedule marker the `user_message` carries and a zone that beats every other
206
+ * source of the message's `timeZone` (`readMessageContext`).
207
+ */
208
+ export interface InternalMessageOptions {
209
+ schedule?: ScheduleMarker;
210
+ timeZoneOverride?: string;
211
+ }
188
212
  /**
189
213
  * 会话:只有所有者可见;每次访问都实时复核 agent 对所有者仍可用(spec 3.6 R5),
190
214
  * 不可用时会话也按不存在处理(`conversation.not_found`)。
@@ -211,6 +235,8 @@ export declare class ConversationsService {
211
235
  private readonly devices;
212
236
  /** A starter's message skips the run's pending questions (reply-while-question spec §8.1). */
213
237
  private readonly questions;
238
+ /** ... and its pending network asks (network ask-to-allow spec §6.7). */
239
+ private readonly approvals;
214
240
  private readonly events;
215
241
  readonly txHost: TransactionHost;
216
242
  constructor(db: KernelDb, eventLog: EventLog, agents: Pick<AgentLookup, "get">, access: Pick<ConversationAccess, "isAccessible">,
@@ -229,7 +255,9 @@ export declare class ConversationsService {
229
255
  /** The device a new conversation is bound to (environments spec §4.1 item 1). */
230
256
  devices: Pick<ConversationDevices, "forNew">,
231
257
  /** A starter's message skips the run's pending questions (reply-while-question spec §8.1). */
232
- questions: Pick<QuestionsService, "skipPendingIn">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost);
258
+ questions: Pick<QuestionsService, "skipPendingIn">,
259
+ /** ... and its pending network asks (network ask-to-allow spec §6.7). */
260
+ approvals: Pick<ApprovalsService, "skipNetworkIn">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost);
233
261
  /**
234
262
  * 不阻塞地调用一次 `ensureRun`(打开 SSE、读事件时):它幂等,并顺带清理孤儿运行,
235
263
  * 会话不会因为某次运行结束后的投递失败而永远停在「运行中」。失败只记日志。
@@ -250,10 +278,12 @@ export declare class ConversationsService {
250
278
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
251
279
  * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
252
280
  * the message when the send switched), so a client can seed the new conversation's log whole.
281
+ * `internal` (kernel callers only): a scheduled task's marker, which also sets
282
+ * `conversations.scheduled_task_id`, and its zone override.
253
283
  */
254
284
  createWithMessage(orgId: string, accountId: string, agentId: string, input: UserMessageInput & {
255
285
  clientMessageId: string;
256
- }, pick?: NewConversationDevice): Promise<{
286
+ }, pick?: NewConversationDevice, internal?: InternalMessageOptions): Promise<{
257
287
  conversation: ConversationView;
258
288
  event: ConversationEvent;
259
289
  events: ConversationEvent[];
@@ -418,18 +448,21 @@ export declare class ConversationsService {
418
448
  /**
419
449
  * Skips, in the caller's transaction (it wrote `accountId`'s message at `messageSeq` and holds the
420
450
  * conversation row lock), the pending questions that message closes (reply-while-question spec
421
- * §8.1). The queue's "send now" uses it as the direct send does; announce after the commit with
422
- * {@link announceSkipped}.
451
+ * §8.1) and the pending network asks of the author's runs, here and in the sub-agents'
452
+ * conversations (network ask-to-allow spec §6.7). The queue's "send now" uses it as the direct
453
+ * send does; announce after the commit with {@link announceSkipped}. `toSeq` is the highest seq
454
+ * written in this conversation (null: none).
423
455
  */
424
- skipQuestionsIn(conversationId: string, accountId: string, messageSeq: number): Promise<{
425
- records: QuestionRecord[];
456
+ skipWaitsIn(conversationId: string, accountId: string, messageSeq: number): Promise<{
457
+ skipped: ReplySkips;
426
458
  toSeq: number | null;
427
459
  }>;
428
460
  /**
429
- * After the commit: announces each skipped question (`question.answered`, outcome `skipped`), so
430
- * its run resumes (waits) and its IM card updates (channel jobs).
461
+ * After the commit: announces each skipped question (`question.answered`, outcome `skipped`) and
462
+ * 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).
431
464
  */
432
- announceSkipped(records: readonly QuestionRecord[]): Promise<void>;
465
+ announceSkipped(skipped: ReplySkips): Promise<void>;
433
466
  /**
434
467
  * Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
435
468
  * `conversations.selection`; returns the events written, in seq order (the `user_message`
@@ -24,6 +24,7 @@ import { ProviderRegistry } from "../runtime/providers.js";
24
24
  import { RunControl as RunControlService } from "../runtime/run-control.js";
25
25
  import { rootConversationOf } from "../runtime/subagents/child.js";
26
26
  import { SkillInvoker } from "../skills/invoke.js";
27
+ import { ApprovalsService } from "../tools/approvals.service.js";
27
28
  import { ConnectorContent } from "../tools/connector-content.js";
28
29
  import { QuestionsService } from "../tools/questions.service.js";
29
30
  import { ConversationAccess, parentReachable } from "./access.js";
@@ -56,6 +57,7 @@ function activitySql(ref) {
56
57
  )`;
57
58
  }
58
59
  const notFound = () => new ApiError("conversation.not_found", "Conversation not found");
60
+ export const NO_REPLY_SKIPS = { questions: [], network: [], seqs: new Map() };
59
61
  export { userMessageIdemKey } from "./idem-keys.js";
60
62
  const COLUMNS = [
61
63
  "id",
@@ -72,6 +74,7 @@ const COLUMNS = [
72
74
  "subagent_type",
73
75
  "archived_at",
74
76
  "device_id",
77
+ "scheduled_task_id",
75
78
  ];
76
79
  function toView(r) {
77
80
  return {
@@ -89,6 +92,7 @@ function toView(r) {
89
92
  subagentType: r.subagent_type,
90
93
  archivedAt: r.archived_at,
91
94
  deviceId: r.device_id,
95
+ scheduledTaskId: r.scheduled_task_id,
92
96
  activity: r.activity ?? null,
93
97
  };
94
98
  }
@@ -133,6 +137,7 @@ let ConversationsService = class ConversationsService {
133
137
  background;
134
138
  devices;
135
139
  questions;
140
+ approvals;
136
141
  events;
137
142
  txHost;
138
143
  constructor(db, eventLog, agents, access,
@@ -151,7 +156,9 @@ let ConversationsService = class ConversationsService {
151
156
  /** The device a new conversation is bound to (environments spec §4.1 item 1). */
152
157
  devices,
153
158
  /** A starter's message skips the run's pending questions (reply-while-question spec §8.1). */
154
- questions, events, txHost) {
159
+ questions,
160
+ /** ... and its pending network asks (network ask-to-allow spec §6.7). */
161
+ approvals, events, txHost) {
155
162
  this.db = db;
156
163
  this.eventLog = eventLog;
157
164
  this.agents = agents;
@@ -165,6 +172,7 @@ let ConversationsService = class ConversationsService {
165
172
  this.background = background;
166
173
  this.devices = devices;
167
174
  this.questions = questions;
175
+ this.approvals = approvals;
168
176
  this.events = events;
169
177
  this.txHost = txHost;
170
178
  }
@@ -207,8 +215,10 @@ let ConversationsService = class ConversationsService {
207
215
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
208
216
  * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
209
217
  * the message when the send switched), so a client can seed the new conversation's log whole.
218
+ * `internal` (kernel callers only): a scheduled task's marker, which also sets
219
+ * `conversations.scheduled_task_id`, and its zone override.
210
220
  */
211
- async createWithMessage(orgId, accountId, agentId, input, pick = {}) {
221
+ async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal = {}) {
212
222
  const agent = await this.agents.get(orgId, agentId, accountId);
213
223
  const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
214
224
  if (prior)
@@ -218,7 +228,7 @@ let ConversationsService = class ConversationsService {
218
228
  const extra = await this.resolveExtra(orgId, accountId, input);
219
229
  const invoked = await this.prepareSkill(orgId, agentId, undefined, agent.definition, input);
220
230
  const id = randomUUID();
221
- const all = await this.createIn({ id, orgId, accountId, agentId, deviceId }, input, next, extra, invoked);
231
+ const all = await this.createIn({ id, orgId, accountId, agentId, deviceId }, input, next, extra, invoked, internal);
222
232
  const written = all?.at(-1);
223
233
  if (!all || !written) {
224
234
  const raced = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
@@ -232,7 +242,7 @@ let ConversationsService = class ConversationsService {
232
242
  }
233
243
  /** The conversation row and its first message (undefined: a concurrent retry committed first). */
234
244
  // its own transaction: the caller notifies and publishes after the commit
235
- async createIn(conv, input, next, extra, invoked) {
245
+ async createIn(conv, input, next, extra, invoked, internal) {
236
246
  const { id, orgId, accountId } = conv;
237
247
  const row = await this.db
238
248
  .insertInto("conversations")
@@ -243,6 +253,7 @@ let ConversationsService = class ConversationsService {
243
253
  account_id: accountId,
244
254
  device_id: conv.deviceId,
245
255
  create_request_id: input.clientMessageId,
256
+ scheduled_task_id: internal.schedule?.taskId ?? null,
246
257
  })
247
258
  .onConflict((oc) => oc
248
259
  .columns(["org_id", "account_id", "create_request_id"])
@@ -255,8 +266,7 @@ let ConversationsService = class ConversationsService {
255
266
  return undefined;
256
267
  const blocks = await this.bindIn(orgId, id, accountId, input.files ?? []);
257
268
  // a new conversation has no run yet, so nothing to skip
258
- return (await this.writeMessage(orgId, id, accountId, input, next, extra, blocks, invoked))
259
- .events;
269
+ return (await this.writeMessage(orgId, id, accountId, input, next, extra, blocks, invoked, internal)).events;
260
270
  }
261
271
  /**
262
272
  * The conversation (and its first event) an earlier first send with this id created. The id used
@@ -494,14 +504,14 @@ let ConversationsService = class ConversationsService {
494
504
  SELECT page.agent_id, page.latest_at, v.definition ->> 'name' AS agent_name,
495
505
  a.profile_avatar, a.profile_updated_at, cs.id, cs.org_id, cs.account_id, cs.title, cs.title_source, cs.selection, cs.last_seq,
496
506
  cs.created_at, cs.updated_at, cs.parent_conversation_id, cs.subagent_type,
497
- cs.archived_at, cs.device_id, cs.activity
507
+ cs.archived_at, cs.device_id, cs.scheduled_task_id, cs.activity
498
508
  FROM page
499
509
  JOIN capybara.agents a ON a.id = page.agent_id
500
510
  JOIN capybara.agent_versions v ON v.agent_id = a.id AND v.version = a.current_version
501
511
  CROSS JOIN LATERAL (
502
512
  SELECT c.id, c.org_id, c.account_id, c.title, c.title_source, c.selection, c.last_seq,
503
513
  c.created_at, c.updated_at, c.parent_conversation_id, c.subagent_type,
504
- c.archived_at, c.device_id, ${activitySql("c")} AS activity
514
+ c.archived_at, c.device_id, c.scheduled_task_id, ${activitySql("c")} AS activity
505
515
  FROM capybara.conversations c
506
516
  WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
507
517
  AND c.deleted_at IS NULL AND ${archivedFilter} ${searchFilter} ${stateFilter}
@@ -798,18 +808,25 @@ let ConversationsService = class ConversationsService {
798
808
  /**
799
809
  * Skips, in the caller's transaction (it wrote `accountId`'s message at `messageSeq` and holds the
800
810
  * conversation row lock), the pending questions that message closes (reply-while-question spec
801
- * §8.1). The queue's "send now" uses it as the direct send does; announce after the commit with
802
- * {@link announceSkipped}.
811
+ * §8.1) and the pending network asks of the author's runs, here and in the sub-agents'
812
+ * conversations (network ask-to-allow spec §6.7). The queue's "send now" uses it as the direct
813
+ * send does; announce after the commit with {@link announceSkipped}. `toSeq` is the highest seq
814
+ * written in this conversation (null: none).
803
815
  */
804
- async skipQuestionsIn(conversationId, accountId, messageSeq) {
805
- return this.questions.skipPendingIn(conversationId, accountId, messageSeq);
816
+ async skipWaitsIn(conversationId, accountId, messageSeq) {
817
+ const q = await this.questions.skipPendingIn(conversationId, accountId, messageSeq);
818
+ const n = await this.approvals.skipNetworkIn(conversationId, accountId);
819
+ const here = n.seqs.get(conversationId);
820
+ const toSeq = q.toSeq === null && here === undefined ? null : Math.max(q.toSeq ?? 0, here ?? 0);
821
+ return { skipped: { questions: q.records, network: n.records, seqs: n.seqs }, toSeq };
806
822
  }
807
823
  /**
808
- * After the commit: announces each skipped question (`question.answered`, outcome `skipped`), so
809
- * its run resumes (waits) and its IM card updates (channel jobs).
824
+ * After the commit: announces each skipped question (`question.answered`, outcome `skipped`) and
825
+ * 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).
810
827
  */
811
- async announceSkipped(records) {
812
- for (const q of records) {
828
+ async announceSkipped(skipped) {
829
+ for (const q of skipped.questions) {
813
830
  await this.events.emit("question.answered", {
814
831
  id: q.id,
815
832
  orgId: q.orgId,
@@ -818,6 +835,18 @@ let ConversationsService = class ConversationsService {
818
835
  outcome: "skipped",
819
836
  });
820
837
  }
838
+ for (const [conversationId, seq] of skipped.seqs) {
839
+ await this.eventLog.notify(conversationId, seq);
840
+ }
841
+ for (const a of skipped.network) {
842
+ await this.events.emit("approval.decided", {
843
+ id: a.id,
844
+ orgId: a.orgId,
845
+ conversationId: a.conversationId,
846
+ runId: a.runId,
847
+ status: "skipped",
848
+ });
849
+ }
821
850
  }
822
851
  /**
823
852
  * Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
@@ -826,7 +855,7 @@ let ConversationsService = class ConversationsService {
826
855
  * questions its author may answer (reply-while-question spec §8.1): `skipped` and the highest
827
856
  * seq their events took. The caller notifies and emits `question.answered` after commit.
828
857
  */
829
- async writeMessage(orgId, conversationId, accountId, input, next, extra, blocks, invoked) {
858
+ async writeMessage(orgId, conversationId, accountId, input, next, extra, blocks, invoked, internal = {}) {
830
859
  const events = [];
831
860
  if (next !== null) {
832
861
  events.push({
@@ -843,13 +872,14 @@ let ConversationsService = class ConversationsService {
843
872
  senderName: input.senderName,
844
873
  browser: { timeZone: input.timeZone, locale: input.locale },
845
874
  im: input.im,
875
+ timeZoneOverride: internal.timeZoneOverride,
846
876
  });
847
- events.push(this.userMessage(accountId, input, extra, blocks, invoked, context));
877
+ events.push(this.userMessage(accountId, input, extra, blocks, invoked, context, internal.schedule));
848
878
  const { events: written, fresh } = await this.eventLog.appendInReport(conversationId, events);
849
879
  const message = written.at(-1);
850
880
  const skips = fresh.at(-1) === true
851
- ? await this.skipQuestionsIn(conversationId, accountId, message.seq)
852
- : { records: [], toSeq: null };
881
+ ? await this.skipWaitsIn(conversationId, accountId, message.seq)
882
+ : { skipped: NO_REPLY_SKIPS, toSeq: null };
853
883
  if (next !== null) {
854
884
  const t = this.db.db;
855
885
  // the cache follows the log (a retried message's switch may be an old, deduped event that a
@@ -860,7 +890,7 @@ let ConversationsService = class ConversationsService {
860
890
  .where("id", "=", conversationId)
861
891
  .execute();
862
892
  }
863
- return { events: written, skipped: skips.records, skippedToSeq: skips.toSeq };
893
+ return { events: written, skipped: skips.skipped, skippedToSeq: skips.toSeq };
864
894
  }
865
895
  /**
866
896
  * Renders the message's skill invocation (phase D), or undefined when it has none. Resolved
@@ -905,7 +935,7 @@ let ConversationsService = class ConversationsService {
905
935
  return [];
906
936
  return this.attachments.bindForMessage(orgId, conversationId, accountId, files);
907
937
  }
908
- userMessage(accountId, input, extra, fileBlocks, invoked, context) {
938
+ userMessage(accountId, input, extra, fileBlocks, invoked, context, schedule) {
909
939
  // a skill invocation's rendered text replaces the typed arguments as content[0]
910
940
  const text = invoked?.text ?? input.text;
911
941
  return {
@@ -926,6 +956,7 @@ let ConversationsService = class ConversationsService {
926
956
  ...(input.clientMessageId !== undefined && { clientMessageId: input.clientMessageId }),
927
957
  ...(input.retryOf !== undefined && { retryOf: input.retryOf }),
928
958
  ...(invoked !== undefined && { skill: invoked.skill }),
959
+ ...(schedule !== undefined && { schedule }),
929
960
  },
930
961
  };
931
962
  }
@@ -988,7 +1019,7 @@ __decorate([
988
1019
  ], ConversationsService.prototype, "postIn", null);
989
1020
  __decorate([
990
1021
  Transactional({ propagation: "mandatory" })
991
- ], ConversationsService.prototype, "skipQuestionsIn", null);
1022
+ ], ConversationsService.prototype, "skipWaitsIn", null);
992
1023
  __decorate([
993
1024
  Transactional({ propagation: "mandatory" })
994
1025
  ], ConversationsService.prototype, "writeMessage", null);
@@ -1010,7 +1041,8 @@ ConversationsService = __decorate([
1010
1041
  __param(10, Inject(BackgroundTaskStopper)),
1011
1042
  __param(11, Inject(ConversationDevices)),
1012
1043
  __param(12, Inject(QuestionsService)),
1013
- __param(13, Inject(KernelEvents)),
1014
- __param(14, Inject(TransactionHost))
1044
+ __param(13, Inject(ApprovalsService)),
1045
+ __param(14, Inject(KernelEvents)),
1046
+ __param(15, Inject(TransactionHost))
1015
1047
  ], ConversationsService);
1016
1048
  export { ConversationsService };