@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
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Public suffixes of more than one label that a device-page wildcard may not cover (a small
3
+ * built-in list, not the public suffix list; capybara-device keeps the same list in
4
+ * packages/device/src/public-suffix.ts). Includes the shared hosting suffixes where each
5
+ * subdomain belongs to someone else.
6
+ */
7
+ const MULTI_LABEL_SUFFIXES = new Set([
8
+ "ac.uk",
9
+ "co.uk",
10
+ "gov.uk",
11
+ "ltd.uk",
12
+ "me.uk",
13
+ "net.uk",
14
+ "org.uk",
15
+ "plc.uk",
16
+ "com.cn",
17
+ "edu.cn",
18
+ "gov.cn",
19
+ "net.cn",
20
+ "org.cn",
21
+ "com.hk",
22
+ "org.hk",
23
+ "com.tw",
24
+ "org.tw",
25
+ "co.jp",
26
+ "ne.jp",
27
+ "or.jp",
28
+ "ac.jp",
29
+ "co.kr",
30
+ "or.kr",
31
+ "com.au",
32
+ "net.au",
33
+ "org.au",
34
+ "edu.au",
35
+ "co.nz",
36
+ "org.nz",
37
+ "com.br",
38
+ "com.mx",
39
+ "com.sg",
40
+ "com.my",
41
+ "co.in",
42
+ "co.za",
43
+ "com.tr",
44
+ "com.ar",
45
+ "github.io",
46
+ "gitlab.io",
47
+ "pages.dev",
48
+ "workers.dev",
49
+ "vercel.app",
50
+ "netlify.app",
51
+ "herokuapp.com",
52
+ "appspot.com",
53
+ "web.app",
54
+ "firebaseapp.com",
55
+ "azurewebsites.net",
56
+ "cloudfront.net",
57
+ "amazonaws.com",
58
+ "blogspot.com",
59
+ "ngrok.io",
60
+ "ngrok-free.app",
61
+ "trycloudflare.com",
62
+ "onrender.com",
63
+ "fly.dev",
64
+ "glitch.me",
65
+ ]);
66
+ /**
67
+ * Why a `*.` wildcard is too broad to add from the device page: its remainder is a whole public
68
+ * suffix (one label such as `com`, or a known multi-label one such as `co.uk`, `github.io`);
69
+ * null for an exact host or a narrower wildcard.
70
+ */
71
+ export function publicSuffixWildcard(pattern) {
72
+ if (!pattern.startsWith("*."))
73
+ return null;
74
+ const rest = pattern.slice(2).toLowerCase();
75
+ if (!rest.includes(".") || MULTI_LABEL_SUFFIXES.has(rest)) {
76
+ return `Use a narrower domain; ${pattern.toLowerCase()} covers a whole public suffix.`;
77
+ }
78
+ return null;
79
+ }
package/dist/errors.d.ts CHANGED
@@ -35,6 +35,7 @@ export declare const ERROR_CODES: {
35
35
  readonly "org.last_owner": 409;
36
36
  readonly "org.owner_required": 403;
37
37
  readonly "org.sandbox_egress_not_allowed": 400;
38
+ readonly "org.sandbox_tier_not_allowed": 400;
38
39
  readonly "member.not_found": 404;
39
40
  readonly "member.already_exists": 409;
40
41
  readonly "member.anonymous_guest": 409;
@@ -231,11 +232,6 @@ export declare const ERROR_CODES: {
231
232
  readonly "agent.model_unavailable": 400;
232
233
  /** A save gave `instructionFiles` and an `instructions` that is not their join. */
233
234
  readonly "agent.instructions_mismatch": 400;
234
- /**
235
- * `skills.pinned` of an agent definition is refused (details: `{ skill, reason, totalBytes? }`;
236
- * reasons `not_selected`, `not_found`, `duplicate`, `too_many`, `too_large`, `manual_only`).
237
- */
238
- readonly "agent.pinned_invalid": 400;
239
235
  /** `subagents` of an agent definition is refused (details: `{ reason, path, value? }`). */
240
236
  readonly "agent.subagents_invalid": 400;
241
237
  /** Creating from a template with a visibility other than `private` without `agents.manage`. */
@@ -291,6 +287,7 @@ export declare const ERROR_CODES: {
291
287
  readonly "approval.not_found": 404;
292
288
  readonly "approval.already_decided": 409;
293
289
  readonly "approval.scope_not_allowed": 409;
290
+ readonly "approval.not_yours": 403;
294
291
  readonly "question.not_found": 404;
295
292
  readonly "question.already_answered": 409;
296
293
  readonly "tool_output.not_found": 404;
@@ -348,6 +345,28 @@ export declare const ERROR_CODES: {
348
345
  readonly "device.offline": 503;
349
346
  /** The local device's machine runs a `capybara-device` too old for this kernel; update it. */
350
347
  readonly "device.program_outdated": 426;
348
+ /**
349
+ * The device's machine refused a network policy change (its local `remoteGrants` is `off`, it
350
+ * runs in Docker mode, or a host is invalid); the message says how to allow it on the machine.
351
+ */
352
+ readonly "device.policy_refused": 409;
353
+ /** The device cannot change its network policy from the platform (cloud, or an older program). */
354
+ readonly "device.policy_unsupported": 409;
355
+ /**
356
+ * The device runs commands without a sandbox (its owner set it locally), which the org or the
357
+ * platform does not allow, or a scheduled run without the task's `tier:open` grant uses it
358
+ * (sandbox dev tiers spec §4.3, §11.4); checked at each command.
359
+ */
360
+ readonly "device.unsandboxed_not_allowed": 403;
361
+ /**
362
+ * A scheduled run uses a cloud device whose sandbox tier is open without the task's `tier:open`
363
+ * grant (sandbox dev tiers spec §11.4).
364
+ */
365
+ readonly "device.tier_not_granted": 403;
366
+ /** A device's sandbox tier above the org's or the platform's highest (sandbox dev tiers §4.1). */
367
+ readonly "device.tier_not_allowed": 400;
368
+ /** A local device's tier is kept on its machine (`policy` op or the local CLI), not the row. */
369
+ readonly "device.tier_on_device": 409;
351
370
  /** Device sign-in (RFC 8628 style): the person has not approved the code yet; keep polling. */
352
371
  readonly "device_sign_in.authorization_pending": 400;
353
372
  /** Device sign-in: the program polls faster than the interval; add 5 seconds and keep polling. */
@@ -362,6 +381,28 @@ export declare const ERROR_CODES: {
362
381
  readonly "device_sign_in.unavailable": 503;
363
382
  /** The machine credential is unknown or revoked. */
364
383
  readonly "device.credential_invalid": 401;
384
+ /** The scheduled task is missing, deleted, in another org or not the caller's to see. */
385
+ readonly "schedule.not_found": 404;
386
+ /** Not a 5-field cron expression (no seconds field). */
387
+ readonly "schedule.invalid_cron": 400;
388
+ /** Not an IANA time zone. */
389
+ readonly "schedule.invalid_time_zone": 400;
390
+ /** A `once` time that is not a local date-time `YYYY-MM-DDTHH:mm[:ss]`. */
391
+ readonly "schedule.invalid_once_time": 400;
392
+ /** A `once` time that is not in the future. */
393
+ readonly "schedule.once_in_past": 400;
394
+ /** Two fire times closer than the org's `minIntervalMinutes`. */
395
+ readonly "schedule.interval_too_short": 400;
396
+ /** The creator or the org has as many active and paused tasks as the org allows. */
397
+ readonly "schedule.limit_reached": 409;
398
+ /** The org turned scheduled tasks off. */
399
+ readonly "schedule.disabled": 409;
400
+ /** Where the task runs is inconsistent or not the creator's to use (conversation, chat, device). */
401
+ readonly "schedule.target_invalid": 400;
402
+ /** A tool the creator cannot pre-approve for the task (spec §7). */
403
+ readonly "schedule.grant_not_allowed": 400;
404
+ /** An org admin may pause, resume and delete another person's task, never edit it. */
405
+ readonly "schedule.forbidden": 403;
365
406
  readonly "skill.not_found": 404;
366
407
  /** Also a skill neither the model nor a person could use (phase D: both invocation flags off). */
367
408
  readonly "skill.invalid_package": 400;
package/dist/errors.js CHANGED
@@ -35,6 +35,7 @@ export const ERROR_CODES = {
35
35
  "org.last_owner": 409,
36
36
  "org.owner_required": 403,
37
37
  "org.sandbox_egress_not_allowed": 400,
38
+ "org.sandbox_tier_not_allowed": 400,
38
39
  "member.not_found": 404,
39
40
  "member.already_exists": 409,
40
41
  "member.anonymous_guest": 409,
@@ -231,11 +232,6 @@ export const ERROR_CODES = {
231
232
  "agent.model_unavailable": 400,
232
233
  /** A save gave `instructionFiles` and an `instructions` that is not their join. */
233
234
  "agent.instructions_mismatch": 400,
234
- /**
235
- * `skills.pinned` of an agent definition is refused (details: `{ skill, reason, totalBytes? }`;
236
- * reasons `not_selected`, `not_found`, `duplicate`, `too_many`, `too_large`, `manual_only`).
237
- */
238
- "agent.pinned_invalid": 400,
239
235
  /** `subagents` of an agent definition is refused (details: `{ reason, path, value? }`). */
240
236
  "agent.subagents_invalid": 400,
241
237
  /** Creating from a template with a visibility other than `private` without `agents.manage`. */
@@ -291,6 +287,7 @@ export const ERROR_CODES = {
291
287
  "approval.not_found": 404,
292
288
  "approval.already_decided": 409,
293
289
  "approval.scope_not_allowed": 409,
290
+ "approval.not_yours": 403,
294
291
  "question.not_found": 404,
295
292
  "question.already_answered": 409,
296
293
  "tool_output.not_found": 404,
@@ -348,6 +345,28 @@ export const ERROR_CODES = {
348
345
  "device.offline": 503,
349
346
  /** The local device's machine runs a `capybara-device` too old for this kernel; update it. */
350
347
  "device.program_outdated": 426,
348
+ /**
349
+ * The device's machine refused a network policy change (its local `remoteGrants` is `off`, it
350
+ * runs in Docker mode, or a host is invalid); the message says how to allow it on the machine.
351
+ */
352
+ "device.policy_refused": 409,
353
+ /** The device cannot change its network policy from the platform (cloud, or an older program). */
354
+ "device.policy_unsupported": 409,
355
+ /**
356
+ * The device runs commands without a sandbox (its owner set it locally), which the org or the
357
+ * platform does not allow, or a scheduled run without the task's `tier:open` grant uses it
358
+ * (sandbox dev tiers spec §4.3, §11.4); checked at each command.
359
+ */
360
+ "device.unsandboxed_not_allowed": 403,
361
+ /**
362
+ * A scheduled run uses a cloud device whose sandbox tier is open without the task's `tier:open`
363
+ * grant (sandbox dev tiers spec §11.4).
364
+ */
365
+ "device.tier_not_granted": 403,
366
+ /** A device's sandbox tier above the org's or the platform's highest (sandbox dev tiers §4.1). */
367
+ "device.tier_not_allowed": 400,
368
+ /** A local device's tier is kept on its machine (`policy` op or the local CLI), not the row. */
369
+ "device.tier_on_device": 409,
351
370
  /** Device sign-in (RFC 8628 style): the person has not approved the code yet; keep polling. */
352
371
  "device_sign_in.authorization_pending": 400,
353
372
  /** Device sign-in: the program polls faster than the interval; add 5 seconds and keep polling. */
@@ -362,6 +381,28 @@ export const ERROR_CODES = {
362
381
  "device_sign_in.unavailable": 503,
363
382
  /** The machine credential is unknown or revoked. */
364
383
  "device.credential_invalid": 401,
384
+ /** The scheduled task is missing, deleted, in another org or not the caller's to see. */
385
+ "schedule.not_found": 404,
386
+ /** Not a 5-field cron expression (no seconds field). */
387
+ "schedule.invalid_cron": 400,
388
+ /** Not an IANA time zone. */
389
+ "schedule.invalid_time_zone": 400,
390
+ /** A `once` time that is not a local date-time `YYYY-MM-DDTHH:mm[:ss]`. */
391
+ "schedule.invalid_once_time": 400,
392
+ /** A `once` time that is not in the future. */
393
+ "schedule.once_in_past": 400,
394
+ /** Two fire times closer than the org's `minIntervalMinutes`. */
395
+ "schedule.interval_too_short": 400,
396
+ /** The creator or the org has as many active and paused tasks as the org allows. */
397
+ "schedule.limit_reached": 409,
398
+ /** The org turned scheduled tasks off. */
399
+ "schedule.disabled": 409,
400
+ /** Where the task runs is inconsistent or not the creator's to use (conversation, chat, device). */
401
+ "schedule.target_invalid": 400,
402
+ /** A tool the creator cannot pre-approve for the task (spec §7). */
403
+ "schedule.grant_not_allowed": 400,
404
+ /** An org admin may pause, resume and delete another person's task, never edit it. */
405
+ "schedule.forbidden": 403,
365
406
  "skill.not_found": 404,
366
407
  /** Also a skill neither the model nor a person could use (phase D: both invocation flags off). */
367
408
  "skill.invalid_package": 400,
@@ -13,15 +13,15 @@ import { type CaseSnapshot } from "./snapshot.js";
13
13
  * - `system_hash` / `tools_changed`(只在基线判定):system 指纹、工具序列(tools 与 catalog)与记录不同;
14
14
  * - `tool_spec_missing`:快照里缺某个工具定义(从请求里去掉);
15
15
  * - `definition_missing`:快照里缺该 agent 版本的定义(用空指令);
16
- * - `pinned_changed` (phase B, never for a baseline): the candidate definition (or candidate agent
17
- * version) pins other skills (`skills.pinned`, deduplicated and capped) than the original agent
18
- * version's definition (the recorded snapshot's `pinned` ids when that definition is missing; not
19
- * with `definition_missing`); the request still renders the recorded pinned bodies (Decision 1);
16
+ * - `pinned_changed` (phase B, never for a baseline, not with `definition_missing`): the
17
+ * `skills_catalog` snapshot in effect at the call pinned skills (`pinned` non-empty). No candidate
18
+ * can pin any more (skills default-on spec §9), so any candidate differs from such a call; the
19
+ * request still renders the recorded pinned bodies (Decision 1);
20
20
  * - `subagent_changed` (phase C, never for a baseline): a sub-agent's (child) case with a candidate
21
21
  * whose effective definition differs from the one frozen in its `subagent_started` (see
22
22
  * {@link subagentChanged}); the request still renders the logged definition and preamble. A child
23
- * case never consults `definitions` (no `definition_missing`) and has no `pinned_changed` (its
24
- * pins are part of the effective definition);
23
+ * case never consults `definitions` (no `definition_missing`) and has no `pinned_changed` (a
24
+ * logged child definition that pinned counts as `subagent_changed`);
25
25
  * - `legacy_event`:记录缺 `tools` / `params` / `absorbedUpToSeq` / `maxOutputTokens`,或还原出媒体而记录里没有
26
26
  * `media` / `mediaBudgetBytes`(较早的事件);
27
27
  * - `params_changed`:没给候选参数时,记录的参数按所用模型的声明清洗后有键被丢弃或值变了(换了模型或模型声明变了);
@@ -31,7 +31,10 @@ import { type CaseSnapshot } from "./snapshot.js";
31
31
  */
32
32
  /** 还原结果与原请求不一致的原因。 */
33
33
  export type DriftReason = "system_hash" | "tools_changed" | "tool_spec_missing" | "definition_missing"
34
- /** The candidate pins other skills than the recorded snapshot rendered (phase B). */
34
+ /**
35
+ * The call rendered pinned skills (phase B), which no candidate has any more (skills default-on
36
+ * spec §9).
37
+ */
35
38
  | "pinned_changed"
36
39
  /** A child case's candidate has another effective definition than the logged one (phase C). */
37
40
  | "subagent_changed" | "legacy_event" | "params_changed"
@@ -97,17 +100,19 @@ export interface SubagentTemplateRef {
97
100
  /**
98
101
  * Does a candidate change what a sub-agent (child) case would run (`subagent_changed`, spec
99
102
  * §15.1)? Pure, from the snapshot alone, comparing the model-visible parts of the effective
100
- * definition (instructions, pinned skills, tools) with the logged one (`logged`, from its
101
- * `subagent_started`):
103
+ * definition (instructions, turned-off skills, tools) with the logged one (`logged`, from its
104
+ * `subagent_started`). A logged definition that pinned skills (`started.definition`, read raw)
105
+ * differs from every candidate: none pins any more (skills default-on spec §9).
102
106
  * - the same agent version (`sameVersion`): the candidate is the logged definition with
103
107
  * overrides (instructions, model, params), compared field by field;
104
108
  * - another version of the case's agent: for an org agent child (`source: "agent"`) that agent's
105
109
  * version, as dispatch would use it (its own tools minus the child-removed ones); otherwise the
106
- * parent's version: `general` takes its instructions and tools and pins nothing, `inline` its
107
- * template of the same name (gone = changed), and a `plugin` template does not depend on the
110
+ * parent's version: `general` takes its instructions and tools (its skills follow the parent's,
111
+ * `parentSide`), `inline` its template of the same name and the parent's turned-off tools and
112
+ * skills (`parentSide`; the template gone = changed), and a `plugin` template does not depend on the
108
113
  * parent version (its own version is {@link ReconstructOptions.subagentTemplate}). Template
109
- * narrowing that needs the database (inline and plugin tool allowlists, skill names) is not
110
- * recomputed: those compare instructions only.
114
+ * narrowing that needs the database (inline and plugin tool allowlists, a plugin template's
115
+ * `disallowedSkills` and skill names) is not recomputed: those compare instructions only.
111
116
  * Models and params are not part of it: they are candidate axes of their own. Neither is the
112
117
  * candidate's `subagents` block as the child's own roster (allowlist, templates, limits; only a
113
118
  * template child's entry in its parent's roster counts, `parentSide`) nor whether it offers `Agent`:
@@ -1,14 +1,24 @@
1
1
  import { sanitizeParams, } from "@capybara-hq/plugin-sdk";
2
- import { AgentDefinitionSchema, builtinDisabled, canonicalDisabledTools, normalizeStoredDefinition, } from "../agents/definition.js";
2
+ import { AgentDefinitionSchema, builtinDisabled, canonicalDisabledSkills, canonicalDisabledTools, normalizeStoredDefinition, } from "../agents/definition.js";
3
3
  import { sameModel, selectionAt } from "../conversations/selection.js";
4
4
  import { ApiError } from "../errors.js";
5
5
  import { mediaRefs, planMedia, } from "../runtime/context/build.js";
6
6
  import { assembleModelRequest } from "../runtime/model-call.js";
7
7
  import { CHILD_REMOVED_TOOLS } from "../runtime/subagents/definition.js";
8
- import { PINNED_SKILLS_LIMITS } from "../skills/catalog.js";
9
8
  import { AGENT_TOOL } from "../tools/builtin-names.js";
10
9
  import { canonicalJson } from "./recorded-tools.js";
11
10
  import { toEvents } from "./snapshot.js";
11
+ /**
12
+ * Does a definition from before skills were on by default pin skills? Read raw: the normalizer
13
+ * drops `skills.pinned` (skills default-on spec §4, §9).
14
+ */
15
+ const pinsSkills = (d) => {
16
+ const skills = typeof d === "object" && d !== null ? d.skills : undefined;
17
+ const pinned = typeof skills === "object" && skills !== null
18
+ ? skills.pinned
19
+ : undefined;
20
+ return Array.isArray(pinned) && pinned.length > 0;
21
+ };
12
22
  const DRIFT_ORDER = [
13
23
  "system_hash",
14
24
  "tools_changed",
@@ -103,27 +113,16 @@ export function reconstructRequest(s, modelCallSeq, opts) {
103
113
  model: { provider: call.payload.provider, model: call.payload.model },
104
114
  });
105
115
  }
106
- // pinned_changed: the candidate pins other skills than the recorded agent version did. Compared
107
- // with the original version's definition, not the effective snapshot (which omits stale or
108
- // oversized pins), both deduplicated and capped like the run; the snapshot's ids only when that
109
- // definition is missing. The stub of definition_missing has no pins, so it is skipped then.
116
+ // pinned_changed (skills default-on spec §9): the snapshot in effect at the call pinned skills,
117
+ // and no candidate pins any more (the migration rewrote the stored versions without pins). The
118
+ // stub of definition_missing is no candidate, so it is skipped then; a child reports a logged
119
+ // pin as subagent_changed instead.
110
120
  if (!baseline && started === undefined && !drift.has("definition_missing")) {
111
- const norm = (ids) => [...new Set(ids ?? [])].slice(0, PINNED_SKILLS_LIMITS.count);
112
- const origDef = s.definitions[String(origVersion)];
113
- let recorded;
114
- if (origDef) {
115
- recorded = norm(normalizeStoredDefinition(origDef).skills?.pinned);
116
- }
117
- else {
118
- const snap = inputEvents(all, call)
119
- .filter((e) => e.type === "skills_catalog")
120
- .at(-1);
121
- recorded = (snap?.payload.pinned ?? []).map((p) => p.id);
122
- }
123
- const wanted = norm(agent.skills?.pinned);
124
- if (recorded.length !== wanted.length || recorded.some((id, i) => wanted[i] !== id)) {
121
+ const snap = inputEvents(all, call)
122
+ .filter((e) => e.type === "skills_catalog")
123
+ .at(-1);
124
+ if ((snap?.payload.pinned ?? []).length > 0)
125
125
  drift.add("pinned_changed");
126
- }
127
126
  }
128
127
  // 工具:按记录的顺序从快照取定义;取不到的从请求里去掉(基线与候选一样)
129
128
  const tools = [];
@@ -215,7 +214,8 @@ const sameTemplate = (logged, t) => logged.source !== "plugin" ||
215
214
  (logged.id === t.id &&
216
215
  logged.version === t.version &&
217
216
  (logged.platformTemplateId ?? null) === (t.platformTemplateId ?? null));
218
- const pinsOf = (d) => [...new Set(d.skills?.pinned ?? [])].slice(0, PINNED_SKILLS_LIMITS.count);
217
+ /** A definition's turned-off skills, as compared (canonical form). */
218
+ const skillsOf = (d) => canonicalJson(canonicalDisabledSkills(d.disabledSkills ?? []));
219
219
  /**
220
220
  * A sub-agent's tools without template narrowing (general and org agents), as compared: its
221
221
  * turned-off tools (tools default-on spec §4) and its key filter's allowlist and `deny` keys,
@@ -233,28 +233,35 @@ const childToolsOf = (d, keepAgent) => canonicalJson({
233
233
  const hasAgent = (d) => !builtinDisabled(d.disabledTools, AGENT_TOOL) &&
234
234
  !(d.subagentTools?.deny ?? []).includes(AGENT_TOOL) &&
235
235
  (d.subagentTools?.allow === undefined || d.subagentTools.allow.includes(AGENT_TOOL));
236
- /** The parent's selection a template child depends on: its tools, org skills and roster entry. */
236
+ /**
237
+ * The parent's selection a template child depends on: its turned-off tools and skills (every
238
+ * `general`, `inline` and `plugin` child starts from them) and its roster entry.
239
+ */
237
240
  const parentSide = (d, started) => canonicalJson({
238
241
  disabledTools: canonicalDisabledTools(d.disabledTools ?? []),
239
- orgSkills: d.skills?.orgSkills ?? [],
242
+ disabledSkills: canonicalDisabledSkills(d.disabledSkills ?? []),
240
243
  listed: started.template.source === "general"
241
244
  ? d.subagents?.general !== false
242
- : (d.subagents?.templates ?? []).includes(started.template.id ?? ""),
245
+ : started.template.source === "inline"
246
+ ? (d.subagents?.inline ?? []).some((x) => x.name === started.agentType)
247
+ : (d.subagents?.templates ?? []).includes(started.template.id ?? ""),
243
248
  });
244
249
  /**
245
250
  * Does a candidate change what a sub-agent (child) case would run (`subagent_changed`, spec
246
251
  * §15.1)? Pure, from the snapshot alone, comparing the model-visible parts of the effective
247
- * definition (instructions, pinned skills, tools) with the logged one (`logged`, from its
248
- * `subagent_started`):
252
+ * definition (instructions, turned-off skills, tools) with the logged one (`logged`, from its
253
+ * `subagent_started`). A logged definition that pinned skills (`started.definition`, read raw)
254
+ * differs from every candidate: none pins any more (skills default-on spec §9).
249
255
  * - the same agent version (`sameVersion`): the candidate is the logged definition with
250
256
  * overrides (instructions, model, params), compared field by field;
251
257
  * - another version of the case's agent: for an org agent child (`source: "agent"`) that agent's
252
258
  * version, as dispatch would use it (its own tools minus the child-removed ones); otherwise the
253
- * parent's version: `general` takes its instructions and tools and pins nothing, `inline` its
254
- * template of the same name (gone = changed), and a `plugin` template does not depend on the
259
+ * parent's version: `general` takes its instructions and tools (its skills follow the parent's,
260
+ * `parentSide`), `inline` its template of the same name and the parent's turned-off tools and
261
+ * skills (`parentSide`; the template gone = changed), and a `plugin` template does not depend on the
255
262
  * parent version (its own version is {@link ReconstructOptions.subagentTemplate}). Template
256
- * narrowing that needs the database (inline and plugin tool allowlists, skill names) is not
257
- * recomputed: those compare instructions only.
263
+ * narrowing that needs the database (inline and plugin tool allowlists, a plugin template's
264
+ * `disallowedSkills` and skill names) is not recomputed: those compare instructions only.
258
265
  * Models and params are not part of it: they are candidate axes of their own. Neither is the
259
266
  * candidate's `subagents` block as the child's own roster (allowlist, templates, limits; only a
260
267
  * template child's entry in its parent's roster counts, `parentSide`) nor whether it offers `Agent`:
@@ -265,24 +272,31 @@ const parentSide = (d, started) => canonicalJson({
265
272
  export function subagentChanged(started, logged, candidate, sameVersion,
266
273
  /** The parent's definition at the case's version (the snapshot's), for template children. */
267
274
  original) {
275
+ if (pinsSkills(started.definition))
276
+ return true;
268
277
  const keepAgent = hasAgent(logged);
269
278
  const childTools = (d) => childToolsOf(d, keepAgent);
270
- const differs = (instructions, pinned, tools) => instructions !== logged.instructions ||
271
- (pinned !== null && canonicalJson(pinned) !== canonicalJson(pinsOf(logged))) ||
279
+ const differs = (instructions, skills, tools) => instructions !== logged.instructions ||
280
+ (skills !== null && skills !== skillsOf(logged)) ||
272
281
  (tools !== null && tools !== childTools(logged));
273
282
  if (sameVersion) {
274
- return differs(candidate.instructions, pinsOf(candidate), childTools(candidate));
283
+ return differs(candidate.instructions, skillsOf(candidate), childTools(candidate));
275
284
  }
276
285
  switch (started.template.source) {
277
286
  case "agent":
278
- return differs(candidate.instructions, pinsOf(candidate), childTools(candidate));
287
+ return differs(candidate.instructions, skillsOf(candidate), childTools(candidate));
279
288
  case "general":
280
- return (differs(candidate.instructions, [], childTools(candidate)) ||
289
+ return (differs(candidate.instructions, null, childTools(candidate)) ||
281
290
  (original !== undefined && parentSide(original, started) !== parentSide(candidate, started)));
282
291
  case "inline": {
283
292
  const t = candidate.subagents?.inline?.find((x) => x.name === started.agentType);
284
293
  if (!t)
285
294
  return true;
295
+ // the parent's turned-off tools and skills reach the child as they do a general one
296
+ if (original !== undefined &&
297
+ parentSide(original, started) !== parentSide(candidate, started)) {
298
+ return true;
299
+ }
286
300
  // conservative: any difference of the template between the two versions counts
287
301
  const o = original?.subagents?.inline?.find((x) => x.name === started.agentType);
288
302
  if (o !== undefined)
@@ -1,5 +1,5 @@
1
- import type { SandboxRoots } from "@capybara-hq/plugin-sdk";
2
1
  import type { EventLog } from "../conversations/event-log.js";
2
+ import { type DevicePathsPayload } from "../runtime/context/device-paths.js";
3
3
  import { type LegacyScope } from "../tools/legacy-keys.js";
4
4
  import { type ToolPipeline, type ToolStepContext } from "../tools/pipeline.js";
5
5
  import type { CaseSnapshot } from "./snapshot.js";
@@ -25,13 +25,13 @@ export interface RecordedCall {
25
25
  /** The recorded call used release-candidate names; `toolKey` and `inputKey` are already the current ones. */
26
26
  legacy?: true;
27
27
  /**
28
- * The roots of the latest `device_paths` event the original run had logged by this call's
29
- * step (environments spec §13.1, ruling R22): set on every recorded call from the step where an
30
- * event was logged onward. A replay that matches the call writes the event when the eval
31
- * conversation's latest one differs, so a candidate whose first sandbox call lands in another
32
- * step still gets the line, once.
28
+ * The payload (roots, and the sandbox level when recorded) of the latest `device_paths` event
29
+ * the original run had logged by this call's step (environments spec §13.1, ruling R22): set on
30
+ * every recorded call from the step where an event was logged onward. A replay that matches the
31
+ * call writes the event when the eval conversation's latest one differs, so a candidate whose
32
+ * first sandbox call lands in another step still gets the line, once.
33
33
  */
34
- devicePaths?: SandboxRoots;
34
+ devicePaths?: DevicePathsPayload;
35
35
  }
36
36
  /**
37
37
  * The connectors and extensions an rc `<x>__<tool>` key can name, from positive evidence in the
@@ -1,4 +1,4 @@
1
- import { devicePathsChange } from "../runtime/context/device-paths.js";
1
+ import { devicePathsChange, devicePathsLevel, } from "../runtime/context/device-paths.js";
2
2
  import { assistantIdemKey } from "../runtime/model-call.js";
3
3
  import { MATCH_IGNORED_FIELDS } from "../tools/builtin-names.js";
4
4
  import { isLegacyPrefixedKey, legacyToolInput, legacyToolKey, } from "../tools/legacy-keys.js";
@@ -149,7 +149,8 @@ export function recordedCalls(s) {
149
149
  for (; next < own.length && own[next].seq < end; next++) {
150
150
  const e = own[next];
151
151
  if (e.type === "device_paths" && e.seq > a) {
152
- current = e.payload.roots;
152
+ const p = e.payload;
153
+ current = { roots: p.roots, ...devicePathsLevel(p) };
153
154
  }
154
155
  }
155
156
  if (current !== undefined)
@@ -238,7 +239,7 @@ export class RecordedToolPipeline {
238
239
  const paths = rec?.devicePaths;
239
240
  const change = paths === undefined
240
241
  ? null
241
- : devicePathsChange(await eventLog.latestOfType(ctx.conversationId, "device_paths"), paths, ctx.runId);
242
+ : devicePathsChange(await eventLog.latestOfType(ctx.conversationId, "device_paths"), paths.roots, ctx.runId, devicePathsLevel(paths));
242
243
  await eventLog.append(ctx.conversationId, [
243
244
  ...(change ? [change] : []),
244
245
  {
@@ -49,7 +49,7 @@ export interface TrajectoryToolCall {
49
49
  approval: {
50
50
  approvalId: string;
51
51
  requestedAt: string;
52
- decision: "approved" | "denied" | "expired" | "cancelled" | null;
52
+ decision: "approved" | "denied" | "expired" | "cancelled" | "skipped" | null;
53
53
  scope?: "once" | "session" | "always";
54
54
  byAccountId: string | null;
55
55
  decidedAt: string | null;
@@ -65,6 +65,11 @@ export interface TrajectoryToolCall {
65
65
  } | null;
66
66
  /** A `tool.preExecute` policy hook denied the call (it never started). */
67
67
  hookDenied?: TrajectoryHookDenial;
68
+ /**
69
+ * The network hosts the command was refused (`network_blocked`, display only; network
70
+ * ask-to-allow spec §6.5). A network ask about them is the call's `approval`.
71
+ */
72
+ networkBlocked?: EventPayload<"network_blocked">["hosts"];
68
73
  /**
69
74
  * An `Agent` call that dispatched (or resumed) a sub-agent (phase C, spec §15.4): the child
70
75
  * conversation (`subagentId`, whose trajectory the viewer links to), the run it started and that
@@ -103,6 +103,7 @@ function buildRun({ started, events }, prices, forks) {
103
103
  const questions = new Map(); // callId → 最近一次
104
104
  const answers = new Map(); // questionId → 结果
105
105
  const toolDenials = new Map(); // callId → denial
106
+ const networkBlocks = new Map(); // callId → refused hosts
106
107
  const modelDenials = new Map(); // step → denial
107
108
  const dispatches = new Map(); // callId → dispatch
108
109
  const childEnds = new Map(); // childRunId → end
@@ -167,6 +168,10 @@ function buildRun({ started, events }, prices, forks) {
167
168
  case "subagent_finished":
168
169
  childEnds.set(e.payload.childRunId, e);
169
170
  break;
171
+ case "network_blocked":
172
+ noteCall(e.payload.step, e.payload.callId);
173
+ networkBlocks.set(e.payload.callId, e);
174
+ break;
170
175
  case "hook_denied":
171
176
  if (e.payload.callId !== undefined) {
172
177
  noteCall(e.payload.step, e.payload.callId);
@@ -234,8 +239,10 @@ function buildRun({ started, events }, prices, forks) {
234
239
  const ans = q === undefined ? undefined : answers.get(q.payload.questionId);
235
240
  const denied = toolDenials.get(callId);
236
241
  const dispatch = dispatches.get(callId)?.payload;
242
+ const blocked = networkBlocks.get(callId)?.payload.hosts;
237
243
  return {
238
244
  ...(denied !== undefined && { hookDenied: denialOf(denied) }),
245
+ ...(blocked !== undefined && { networkBlocked: blocked }),
239
246
  ...(dispatch !== undefined && {
240
247
  subagent: {
241
248
  subagentId: dispatch.subagentId,