@capybara-hq/kernel 0.1.0-rc.21 → 0.1.0-rc.23

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 (263) hide show
  1. package/dist/admin/admin.controller.d.ts +3 -3
  2. package/dist/admin/admin.controller.js +6 -2
  3. package/dist/admin/admin.schemas.d.ts +13 -0
  4. package/dist/agents/admin-agents.d.ts +16 -3
  5. package/dist/agents/admin-agents.js +65 -7
  6. package/dist/agents/agents.schemas.d.ts +24 -6
  7. package/dist/agents/agents.service.d.ts +4 -3
  8. package/dist/agents/agents.service.js +39 -26
  9. package/dist/agents/definition.d.ts +44 -6
  10. package/dist/agents/definition.js +86 -18
  11. package/dist/agents/from-template.d.ts +6 -3
  12. package/dist/agents/from-template.js +57 -44
  13. package/dist/agents/org-templates.js +97 -41
  14. package/dist/agents/templates.d.ts +6 -0
  15. package/dist/agents/templates.js +15 -1
  16. package/dist/background/launch.d.ts +43 -5
  17. package/dist/background/launch.js +88 -8
  18. package/dist/background/session.d.ts +14 -2
  19. package/dist/background/session.js +26 -4
  20. package/dist/background/status.d.ts +1 -1
  21. package/dist/background/status.js +3 -3
  22. package/dist/background/stop.d.ts +0 -2
  23. package/dist/background/stop.js +1 -21
  24. package/dist/catalog/admin-catalog.controller.d.ts +2 -4
  25. package/dist/catalog/admin-catalog.controller.js +2 -6
  26. package/dist/catalog/catalog-content.d.ts +5 -0
  27. package/dist/catalog/catalog-content.js +1 -0
  28. package/dist/catalog/catalog.module.js +3 -0
  29. package/dist/catalog/claude/report.d.ts +9 -2
  30. package/dist/catalog/claude/report.js +1 -0
  31. package/dist/catalog/claude/translate.js +1 -1
  32. package/dist/catalog/component-promotion.js +1 -2
  33. package/dist/catalog/component-state.d.ts +13 -1
  34. package/dist/catalog/component-state.js +14 -5
  35. package/dist/catalog/marketplaces.service.d.ts +1 -1
  36. package/dist/catalog/marketplaces.service.js +2 -1
  37. package/dist/catalog/org-catalog.controller.d.ts +13 -3
  38. package/dist/catalog/org-catalog.controller.js +26 -4
  39. package/dist/catalog/org-catalog.schemas.d.ts +13 -0
  40. package/dist/catalog/org-catalog.schemas.js +34 -0
  41. package/dist/catalog/org-plugin-uploads.controller.js +0 -1
  42. package/dist/catalog/org-plugin-uploads.d.ts +0 -1
  43. package/dist/catalog/org-plugin-uploads.js +0 -3
  44. package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
  45. package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
  46. package/dist/catalog/platform-components.d.ts +0 -1
  47. package/dist/catalog/platform-components.js +3 -8
  48. package/dist/catalog/plugin-import.d.ts +0 -8
  49. package/dist/catalog/plugin-import.js +4 -35
  50. package/dist/catalog/plugin-install.d.ts +2 -2
  51. package/dist/catalog/plugin-install.js +2 -2
  52. package/dist/catalog/plugin-package.d.ts +7 -0
  53. package/dist/catalog/plugin-package.js +27 -1
  54. package/dist/catalog/plugin-upgrade.d.ts +5 -6
  55. package/dist/catalog/plugin-upgrade.js +5 -23
  56. package/dist/catalog/requests.service.d.ts +66 -10
  57. package/dist/catalog/requests.service.js +196 -15
  58. package/dist/channels/actions.d.ts +2 -0
  59. package/dist/channels/actions.js +19 -4
  60. package/dist/channels/cards.d.ts +16 -2
  61. package/dist/channels/cards.js +40 -7
  62. package/dist/channels/channels.module.js +3 -0
  63. package/dist/channels/outbound.d.ts +22 -1
  64. package/dist/channels/outbound.js +116 -7
  65. package/dist/channels/phrases.d.ts +25 -0
  66. package/dist/channels/phrases.js +19 -0
  67. package/dist/channels/threads.d.ts +12 -0
  68. package/dist/channels/threads.js +34 -0
  69. package/dist/config/schema.d.ts +23 -1
  70. package/dist/config/schema.js +107 -5
  71. package/dist/conversations/conversations.service.d.ts +42 -9
  72. package/dist/conversations/conversations.service.js +57 -25
  73. package/dist/conversations/events.d.ts +106 -11
  74. package/dist/conversations/events.js +121 -18
  75. package/dist/conversations/message-context.d.ts +6 -3
  76. package/dist/conversations/message-context.js +14 -10
  77. package/dist/conversations/queue.d.ts +15 -2
  78. package/dist/conversations/queue.js +22 -3
  79. package/dist/conversations/queue.service.d.ts +12 -6
  80. package/dist/conversations/queue.service.js +36 -13
  81. package/dist/db/kernel-schema.d.ts +105 -3
  82. package/dist/devices/conversation-devices.d.ts +8 -1
  83. package/dist/devices/conversation-devices.js +8 -0
  84. package/dist/devices/devices.controller.d.ts +21 -4
  85. package/dist/devices/devices.controller.js +46 -8
  86. package/dist/devices/devices.schemas.d.ts +9 -1
  87. package/dist/devices/devices.schemas.js +11 -3
  88. package/dist/devices/devices.service.d.ts +176 -4
  89. package/dist/devices/devices.service.js +347 -6
  90. package/dist/devices/dto.d.ts +56 -3
  91. package/dist/devices/dto.js +51 -1
  92. package/dist/devices/public-suffix.d.ts +6 -0
  93. package/dist/devices/public-suffix.js +79 -0
  94. package/dist/errors.d.ts +50 -5
  95. package/dist/errors.js +50 -5
  96. package/dist/evals/reconstruct.d.ts +18 -13
  97. package/dist/evals/reconstruct.js +50 -36
  98. package/dist/evals/recorded-tools.d.ts +7 -7
  99. package/dist/evals/recorded-tools.js +4 -3
  100. package/dist/evals/trajectory.d.ts +6 -1
  101. package/dist/evals/trajectory.js +7 -0
  102. package/dist/extensions/package.js +2 -2
  103. package/dist/http/api.module.js +5 -0
  104. package/dist/identity/settings.d.ts +53 -3
  105. package/dist/identity/settings.js +92 -9
  106. package/dist/inbox/inbox.module.js +2 -1
  107. package/dist/inbox/inbox.service.d.ts +19 -4
  108. package/dist/inbox/inbox.service.js +14 -3
  109. package/dist/infra/config/kernel-config.service.d.ts +7 -0
  110. package/dist/infra/config/kernel-config.service.js +31 -0
  111. package/dist/infra/events/kernel-events.d.ts +2 -1
  112. package/dist/local-devices/device-policy.service.d.ts +65 -0
  113. package/dist/local-devices/device-policy.service.js +217 -0
  114. package/dist/local-devices/local-device-gateway.d.ts +4 -2
  115. package/dist/local-devices/local-device-gateway.js +39 -11
  116. package/dist/local-devices/local-device-provider.d.ts +40 -3
  117. package/dist/local-devices/local-device-provider.js +109 -7
  118. package/dist/local-devices/local-devices.module.d.ts +2 -1
  119. package/dist/local-devices/local-devices.module.js +19 -3
  120. package/dist/local-devices/machines.d.ts +30 -3
  121. package/dist/local-devices/machines.js +79 -3
  122. package/dist/local-devices/network-grants.d.ts +104 -0
  123. package/dist/local-devices/network-grants.js +288 -0
  124. package/dist/local-devices/policy-summary.d.ts +20 -0
  125. package/dist/local-devices/policy-summary.js +63 -0
  126. package/dist/orgs/orgs.module.js +4 -10
  127. package/dist/orgs/roles.d.ts +1 -1
  128. package/dist/orgs/roles.js +4 -0
  129. package/dist/orgs/settings.controller.js +4 -3
  130. package/dist/orgs/settings.service.d.ts +172 -14
  131. package/dist/orgs/settings.service.js +229 -27
  132. package/dist/platform/exclusions.js +11 -1
  133. package/dist/platform/operation.js +4 -0
  134. package/dist/platform/ops/admin-items.ops.js +1 -1
  135. package/dist/platform/ops/admin.ops.js +32 -7
  136. package/dist/platform/ops/agent-skills.ops.js +2 -2
  137. package/dist/platform/ops/agent-templates.ops.js +2 -2
  138. package/dist/platform/ops/agents.ops.js +11 -15
  139. package/dist/platform/ops/devices.ops.js +3 -1
  140. package/dist/platform/ops/inbox.ops.js +19 -0
  141. package/dist/platform/ops/org-settings.ops.js +15 -6
  142. package/dist/platform/ops/orgs.ops.js +1 -0
  143. package/dist/platform/ops/platform.ops.d.ts +9 -1
  144. package/dist/platform/ops/platform.ops.js +34 -12
  145. package/dist/platform/ops/requests.ops.js +3 -2
  146. package/dist/platform/ops/schedules.ops.d.ts +21 -0
  147. package/dist/platform/ops/schedules.ops.js +470 -0
  148. package/dist/platform/ops/skills.ops.js +3 -3
  149. package/dist/platform/platform-ops.module.d.ts +2 -1
  150. package/dist/platform/platform-ops.module.js +5 -0
  151. package/dist/platform/route-schemas.js +3 -0
  152. package/dist/roles/worker.module.js +3 -0
  153. package/dist/runtime/context/build.js +13 -4
  154. package/dist/runtime/context/compaction.js +6 -3
  155. package/dist/runtime/context/device-paths.d.ts +43 -21
  156. package/dist/runtime/context/device-paths.js +104 -21
  157. package/dist/runtime/context/run-context.d.ts +9 -0
  158. package/dist/runtime/context/run-context.js +8 -0
  159. package/dist/runtime/context/skills.d.ts +14 -1
  160. package/dist/runtime/context/skills.js +36 -5
  161. package/dist/runtime/maintenance.d.ts +4 -2
  162. package/dist/runtime/maintenance.js +16 -5
  163. package/dist/runtime/run-finish.d.ts +6 -1
  164. package/dist/runtime/run-finish.js +16 -2
  165. package/dist/runtime/run-queue.js +21 -2
  166. package/dist/runtime/run-worker.js +14 -33
  167. package/dist/runtime/runtime.module.js +3 -0
  168. package/dist/runtime/subagents/definition.d.ts +32 -14
  169. package/dist/runtime/subagents/definition.js +106 -109
  170. package/dist/runtime/subagents/dispatch.js +4 -3
  171. package/dist/runtime/subagents/preamble.d.ts +2 -1
  172. package/dist/runtime/subagents/preamble.js +2 -1
  173. package/dist/runtime/waits.js +14 -4
  174. package/dist/sandbox/platform-tiers.d.ts +86 -0
  175. package/dist/sandbox/platform-tiers.js +97 -0
  176. package/dist/sandbox/sandbox.service.d.ts +121 -11
  177. package/dist/sandbox/sandbox.service.js +351 -30
  178. package/dist/sandbox/tiers.d.ts +98 -0
  179. package/dist/sandbox/tiers.js +104 -0
  180. package/dist/schedules/effective-settings.d.ts +16 -0
  181. package/dist/schedules/effective-settings.js +17 -0
  182. package/dist/schedules/errors.d.ts +8 -0
  183. package/dist/schedules/errors.js +8 -0
  184. package/dist/schedules/fire.d.ts +118 -0
  185. package/dist/schedules/fire.js +625 -0
  186. package/dist/schedules/grant-rules.d.ts +94 -0
  187. package/dist/schedules/grant-rules.js +126 -0
  188. package/dist/schedules/job-ids.d.ts +32 -0
  189. package/dist/schedules/job-ids.js +27 -0
  190. package/dist/schedules/pause.d.ts +9 -0
  191. package/dist/schedules/pause.js +33 -0
  192. package/dist/schedules/run-tracker.d.ts +81 -0
  193. package/dist/schedules/run-tracker.js +333 -0
  194. package/dist/schedules/scan.job.d.ts +64 -0
  195. package/dist/schedules/scan.job.js +303 -0
  196. package/dist/schedules/schedule-math.d.ts +31 -0
  197. package/dist/schedules/schedule-math.js +223 -0
  198. package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
  199. package/dist/schedules/scheduled-tasks.service.js +1193 -0
  200. package/dist/schedules/schedules.controller.d.ts +68 -0
  201. package/dist/schedules/schedules.controller.js +242 -0
  202. package/dist/schedules/schedules.module.d.ts +16 -0
  203. package/dist/schedules/schedules.module.js +65 -0
  204. package/dist/schedules/schedules.schemas.d.ts +316 -0
  205. package/dist/schedules/schedules.schemas.js +186 -0
  206. package/dist/schedules/store.d.ts +81 -0
  207. package/dist/schedules/store.js +390 -0
  208. package/dist/schedules/store.module.d.ts +6 -0
  209. package/dist/schedules/store.module.js +21 -0
  210. package/dist/schedules/types.d.ts +108 -0
  211. package/dist/schedules/types.js +1 -0
  212. package/dist/schedules/unattended.d.ts +117 -0
  213. package/dist/schedules/unattended.js +228 -0
  214. package/dist/setup/oauth-token-probe.js +8 -4
  215. package/dist/skills/agent-skills.service.d.ts +4 -8
  216. package/dist/skills/agent-skills.service.js +18 -53
  217. package/dist/skills/catalog.d.ts +65 -62
  218. package/dist/skills/catalog.js +109 -206
  219. package/dist/skills/conversation-catalog.d.ts +3 -3
  220. package/dist/skills/conversation-catalog.js +5 -12
  221. package/dist/skills/invoke.d.ts +2 -2
  222. package/dist/skills/skills.controller.d.ts +3 -1
  223. package/dist/skills/skills.service.d.ts +38 -37
  224. package/dist/skills/skills.service.js +168 -107
  225. package/dist/skills/usable.d.ts +57 -8
  226. package/dist/skills/usable.js +172 -38
  227. package/dist/tools/approvals.controller.d.ts +4 -2
  228. package/dist/tools/approvals.controller.js +17 -2
  229. package/dist/tools/approvals.schemas.d.ts +1 -0
  230. package/dist/tools/approvals.schemas.js +3 -1
  231. package/dist/tools/approvals.service.d.ts +182 -7
  232. package/dist/tools/approvals.service.js +597 -13
  233. package/dist/tools/builtin-names.d.ts +25 -4
  234. package/dist/tools/builtin-names.js +45 -4
  235. package/dist/tools/builtins/builtin-tools.d.ts +8 -0
  236. package/dist/tools/builtins/builtin-tools.js +23 -2
  237. package/dist/tools/builtins/sandbox/bash.js +16 -7
  238. package/dist/tools/builtins/sandbox/common.d.ts +1 -1
  239. package/dist/tools/builtins/sandbox/common.js +33 -10
  240. package/dist/tools/builtins/sandbox/edit.js +7 -3
  241. package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
  242. package/dist/tools/builtins/sandbox/file-state.js +33 -3
  243. package/dist/tools/builtins/sandbox/write.js +8 -4
  244. package/dist/tools/builtins/schedules.d.ts +107 -0
  245. package/dist/tools/builtins/schedules.js +789 -0
  246. package/dist/tools/elicitation.d.ts +2 -0
  247. package/dist/tools/elicitation.js +1 -1
  248. package/dist/tools/fixed-ask.d.ts +7 -0
  249. package/dist/tools/fixed-ask.js +9 -0
  250. package/dist/tools/network-asks.d.ts +109 -0
  251. package/dist/tools/network-asks.js +221 -0
  252. package/dist/tools/pipeline.d.ts +53 -6
  253. package/dist/tools/pipeline.js +202 -13
  254. package/dist/tools/policy.d.ts +21 -8
  255. package/dist/tools/policy.js +63 -9
  256. package/dist/tools/registry.d.ts +10 -1
  257. package/dist/tools/tools.module.js +12 -0
  258. package/package.json +2 -2
  259. package/src/migrations/0113_scheduled_tasks.sql +108 -0
  260. package/src/migrations/0114_network_asks.sql +23 -0
  261. package/src/migrations/0115_network_requests.sql +28 -0
  262. package/src/migrations/0116_skills_default_on.sql +37 -0
  263. package/src/migrations/0117_sandbox_tiers.sql +11 -0
@@ -75,6 +75,8 @@ export declare class ElicitationSession {
75
75
  } | undefined;
76
76
  onSuspend: () => void;
77
77
  declineAll?: boolean;
78
+ /** With `declineAll`: the note added to the result instead of the default one. */
79
+ declineNote?: string;
78
80
  });
79
81
  handle: (req: ElicitationRequest) => Promise<ElicitationResponse>;
80
82
  }
@@ -239,7 +239,7 @@ export class ElicitationSession {
239
239
  }
240
240
  handle = async (req) => {
241
241
  if (this.opts.declineAll) {
242
- this.notes.push(unsupportedNote("this tool cannot pause for user input"));
242
+ this.notes.push(this.opts.declineNote ?? unsupportedNote("this tool cannot pause for user input"));
243
243
  return { action: "decline" };
244
244
  }
245
245
  const fp = fingerprint(req);
@@ -0,0 +1,7 @@
1
+ import type { ResolvedTool } from "./registry.js";
2
+ /**
3
+ * Whether the tool is a fixed ask: its spec declares `requiresUserInteraction`, or it is on the R3
4
+ * list ({@link R3_FIXED_ASK_TOOL_KEYS}: the schedule writes). Its policy is at least `ask` and
5
+ * remembered grants never let it through.
6
+ */
7
+ export declare function isFixedAsk(tool: ResolvedTool): boolean;
@@ -0,0 +1,9 @@
1
+ import { R3_FIXED_ASK_TOOL_KEYS } from "./builtin-names.js";
2
+ /**
3
+ * Whether the tool is a fixed ask: its spec declares `requiresUserInteraction`, or it is on the R3
4
+ * list ({@link R3_FIXED_ASK_TOOL_KEYS}: the schedule writes). Its policy is at least `ask` and
5
+ * remembered grants never let it through.
6
+ */
7
+ export function isFixedAsk(tool) {
8
+ return (tool.spec.annotations?.requiresUserInteraction === true || R3_FIXED_ASK_TOOL_KEYS.has(tool.key));
9
+ }
@@ -0,0 +1,109 @@
1
+ import { type DeviceFeature, type DeviceLocalPolicySummary, type NetworkBlockedHost } from "@capybara-hq/plugin-sdk";
2
+ import type { NetworkRunState } from "../local-devices/network-grants.js";
3
+ import { type UnattendedRun } from "../schedules/unattended.js";
4
+ import { type ApprovalRecord } from "./approvals.service.js";
5
+ /**
6
+ * Network asks in the tool pipeline (network ask-to-allow spec §6.2, §6.6–§6.9, §8.3): what a
7
+ * command's reported refusals turn into (an ask to the device owner, or notes written at once) and
8
+ * the model-visible notes. Pure: the pipeline reads the facts and writes the events.
9
+ */
10
+ /** The most network asks one run makes (spec §6.8); later refusals get {@link tooManyNote}. */
11
+ export declare const MAX_NETWORK_ASKS_PER_RUN = 5;
12
+ /** The longest kept command result of a network ask, in UTF-8 bytes (spec §6.2). */
13
+ export declare const NETWORK_ASK_KEPT_MAX_BYTES: number;
14
+ /** The line a kept result ends with when it had to be clipped to {@link NETWORK_ASK_KEPT_MAX_BYTES}. */
15
+ export declare const NETWORK_ASK_CLIPPED_NOTE = "[Output clipped to its first 16 KB while waiting for network access.]";
16
+ /** The idempotency key of a call's `network_blocked` event. */
17
+ export declare const networkBlockedIdemKey: (runId: string, callId: string) => string;
18
+ /** Asked, allowed this time (a run grant; also the 「一直允许」 that fell back to one). */
19
+ export declare const allowedOnceNote: (hosts: readonly string[]) => string;
20
+ /** Asked, allowed on the device from now on. */
21
+ export declare const allowedAlwaysNote: (hosts: readonly string[]) => string;
22
+ /** Asked and declined (also the unchecked hosts of a partial decision, and a host refused earlier in the run). */
23
+ export declare const deniedNote: (hosts: readonly string[], reason?: string | null) => string;
24
+ /** Asked, no answer within the approval timeout. */
25
+ export declare const expiredNote: (hosts: readonly string[], hours: number) => string;
26
+ /** Asked, the person sent a message instead (spec §6.7). */
27
+ export declare const skippedNote: (hosts: readonly string[]) => string;
28
+ /** Asked, the run was stopped before the answer (the note abort writes too). */
29
+ export declare const cancelledNote: () => string;
30
+ /** Not asked: the run tree already waits for another network ask (at most one, spec §6.8). */
31
+ export declare const otherAskPendingNote: (hosts: readonly string[]) => string;
32
+ /** Not asked: the run made {@link MAX_NETWORK_ASKS_PER_RUN} asks already (spec §6.8). */
33
+ export declare const tooManyNote: (hosts: readonly string[]) => string;
34
+ /**
35
+ * Not asked: the device takes no network changes from the deployment (`remoteGrants: off`), so
36
+ * neither a run grant nor 「一直允许」 would reach it; only its owner can allow the host locally.
37
+ */
38
+ export declare const localOnlyNote: (hosts: readonly string[]) => string;
39
+ /**
40
+ * Asked and allowed 「一直允许」, but the device refused the policy op (outcome `refused`): nothing
41
+ * was granted.
42
+ */
43
+ export declare const alwaysRefusedNote: (hosts: readonly string[]) => string;
44
+ /** The org layer (spec §6.6): only an org admin can lift it. */
45
+ export declare const orgLayerNote: (hosts: readonly string[]) => string;
46
+ /** The address layer (spec §6.6): nobody can lift it. */
47
+ export declare const addressLayerNote: (hosts: readonly string[]) => string;
48
+ /**
49
+ * An unattended run (spec §8.1): the task approves the host, yet the device refused it: the device
50
+ * takes no network grants from the deployment (`remoteGrants` off, or a program too old for them).
51
+ * Not asked, not a missing grant: only the device's owner can allow it locally.
52
+ */
53
+ export declare const taskGrantIgnoredNote: (hosts: readonly string[]) => string;
54
+ /** An unattended run in `fail` mode (spec §8.3): no ask, the creator can add a task grant. */
55
+ export declare const unattendedNetworkNote: (hosts: readonly string[]) => string;
56
+ /** What the pipeline knows when a command reported refused hosts. */
57
+ export interface NetworkPlanContext {
58
+ /** The device's active machine: its features and local policy summary (null: none connected). */
59
+ device: {
60
+ features: readonly DeviceFeature[];
61
+ policy: DeviceLocalPolicySummary | null;
62
+ } | null;
63
+ /** The scheduled task run the run serves (null / undefined: attended). */
64
+ unattended: UnattendedRun | null | undefined;
65
+ }
66
+ /** What to do with a command's refused hosts. */
67
+ export interface NetworkPlan {
68
+ /** The device-layer hosts to ask the device owner about (empty: no ask). */
69
+ ask: {
70
+ host: string;
71
+ port: number;
72
+ }[];
73
+ /** The scopes the ask offers (a device without remote grants is never asked). */
74
+ scopes: ("once" | "always")[];
75
+ /** Notes written now, after the command output (one line per outcome). */
76
+ notes: string[];
77
+ /** `missing_grants` keys of an unattended run (`network:<host>`). */
78
+ missingGrants: string[];
79
+ }
80
+ /** The hosts of a `network_blocked` event: valid, deduplicated, at most {@link NETWORK_ASK_MAX_HOSTS}. */
81
+ export declare function blockedEventHosts(blocked: readonly NetworkBlockedHost[]): NetworkBlockedHost[];
82
+ /**
83
+ * Decides what a command's refused hosts become (spec §6.2, §6.8, §6.9, §8.3), given the run
84
+ * tree's network asks (`run`). The caller asks only for foreground `Bash` on a device; this decides
85
+ * the rest:
86
+ * - `address` and `org` layers: a note each (in an unattended run an `org` host is a missing grant);
87
+ * `unknown`: nothing (the device's trailer explains);
88
+ * - `device` layer on a program without `policyOp`: nothing (no ask, the trailer explains);
89
+ * - approved by the scheduled task the run serves (`run.taskGranted`) yet refused: a note that
90
+ * the device ignores the task's grants (never asked, never a missing grant);
91
+ * - refused earlier in the run tree: the denied note; an unattended run in `fail` mode: its note
92
+ * and a missing grant;
93
+ * - the rest is asked, unless another ask of the run tree is pending or the run made
94
+ * {@link MAX_NETWORK_ASKS_PER_RUN} asks (a note each).
95
+ */
96
+ export declare function planNetworkAsk(blocked: readonly NetworkBlockedHost[], run: NetworkRunState | null, ctx: NetworkPlanContext): NetworkPlan;
97
+ /**
98
+ * The note of a settled network ask (spec §6.6): one line per outcome. An approval allows
99
+ * `detail.grantedHosts` (once, always, or once after 「一直允许」 fell back) and declines the hosts
100
+ * left unchecked; `hours` is the ask's own wait (created to expiry). Null while it is pending.
101
+ */
102
+ export declare function settledNetworkNotes(a: ApprovalRecord, hours: number): string[] | null;
103
+ /** `text` followed by `notes` after a blank line (unchanged without notes). */
104
+ export declare const withNetworkNotes: (text: string, notes: readonly string[]) => string;
105
+ /**
106
+ * A kept result's content within {@link NETWORK_ASK_KEPT_MAX_BYTES} (UTF-8): the head, then
107
+ * {@link NETWORK_ASK_CLIPPED_NOTE}. The caller archives large output first, so this rarely cuts.
108
+ */
109
+ export declare function clipKept(content: string): string;
@@ -0,0 +1,221 @@
1
+ import { validateAllowHost, } from "@capybara-hq/plugin-sdk";
2
+ import { NETWORK_ASK_MAX_HOSTS, NETWORK_HOST_MAX_CHARS } from "../conversations/events.js";
3
+ import { networkGrantKey, unattendedFails, unattendedRules, } from "../schedules/unattended.js";
4
+ import { NETWORK_ASK_CANCELLED_NOTE } from "./approvals.service.js";
5
+ /**
6
+ * Network asks in the tool pipeline (network ask-to-allow spec §6.2, §6.6–§6.9, §8.3): what a
7
+ * command's reported refusals turn into (an ask to the device owner, or notes written at once) and
8
+ * the model-visible notes. Pure: the pipeline reads the facts and writes the events.
9
+ */
10
+ /** The most network asks one run makes (spec §6.8); later refusals get {@link tooManyNote}. */
11
+ export const MAX_NETWORK_ASKS_PER_RUN = 5;
12
+ /** The longest kept command result of a network ask, in UTF-8 bytes (spec §6.2). */
13
+ export const NETWORK_ASK_KEPT_MAX_BYTES = 16 * 1024;
14
+ /** The line a kept result ends with when it had to be clipped to {@link NETWORK_ASK_KEPT_MAX_BYTES}. */
15
+ export const NETWORK_ASK_CLIPPED_NOTE = "[Output clipped to its first 16 KB while waiting for network access.]";
16
+ /** The idempotency key of a call's `network_blocked` event. */
17
+ export const networkBlockedIdemKey = (runId, callId) => `network_blocked:${runId}:${callId}`;
18
+ const list = (hosts) => hosts.join(", ");
19
+ const one = (hosts) => hosts.length === 1;
20
+ const pron = (hosts) => (one(hosts) ? "it" : "them");
21
+ /** "x was blocked by this device's network policy." (the lead of every device-layer note). */
22
+ const blockedLead = (hosts) => `[network] ${list(hosts)} ${one(hosts) ? "was" : "were"} blocked by this device's network policy.`;
23
+ /** Asked, allowed this time (a run grant; also the 「一直允许」 that fell back to one). */
24
+ export const allowedOnceNote = (hosts) => `${blockedLead(hosts)} The user allowed ${pron(hosts)} for the rest of this task. Run the command again.`;
25
+ /** Asked, allowed on the device from now on. */
26
+ export const allowedAlwaysNote = (hosts) => `${blockedLead(hosts)} The user allowed ${pron(hosts)} on this device from now on. Run the command again.`;
27
+ /** Asked and declined (also the unchecked hosts of a partial decision, and a host refused earlier in the run). */
28
+ export const deniedNote = (hosts, reason) => `${blockedLead(hosts)} The user declined access to ${list(hosts)}. Do not try to reach ${pron(hosts)} another way; continue without ${pron(hosts)} or ask the user.` +
29
+ (reason != null && reason !== "" ? ` Reason: ${reason}` : "");
30
+ /** Asked, no answer within the approval timeout. */
31
+ export const expiredNote = (hosts, hours) => `${blockedLead(hosts)} The user did not answer within ${hours}h; access stays blocked.`;
32
+ /** Asked, the person sent a message instead (spec §6.7). */
33
+ export const skippedNote = (hosts) => `${blockedLead(hosts)} The user sent a message instead of answering; access stays blocked.`;
34
+ /** Asked, the run was stopped before the answer (the note abort writes too). */
35
+ export const cancelledNote = () => NETWORK_ASK_CANCELLED_NOTE;
36
+ /** Not asked: the run tree already waits for another network ask (at most one, spec §6.8). */
37
+ export const otherAskPendingNote = (hosts) => `${blockedLead(hosts).slice(0, -1)}; another network request is waiting for the user.`;
38
+ /** Not asked: the run made {@link MAX_NETWORK_ASKS_PER_RUN} asks already (spec §6.8). */
39
+ export const tooManyNote = (hosts) => `${blockedLead(hosts)} Too many network requests in this task; ask the user to allow the domains on the device page.`;
40
+ /** The local command that allows each host (one `policy add` per host). */
41
+ const localAdd = (hosts) => hosts.map((h) => `capybara-device policy add allowedDomains ${h}`).join(" and ");
42
+ /**
43
+ * Not asked: the device takes no network changes from the deployment (`remoteGrants: off`), so
44
+ * neither a run grant nor 「一直允许」 would reach it; only its owner can allow the host locally.
45
+ */
46
+ export const localOnlyNote = (hosts) => `${blockedLead(hosts).slice(0, -1)}, and this device does not accept network changes from the conversation. The device owner can allow ${pron(hosts)} locally: ${localAdd(hosts)}. Continue without ${pron(hosts)} or tell the user.`;
47
+ /**
48
+ * Asked and allowed 「一直允许」, but the device refused the policy op (outcome `refused`): nothing
49
+ * was granted.
50
+ */
51
+ export const alwaysRefusedNote = (hosts) => `${blockedLead(hosts)} The user allowed ${pron(hosts)} on this device, but the device does not accept network changes from the deployment; access stays blocked. The device owner can allow ${pron(hosts)} locally: ${localAdd(hosts)}.`;
52
+ /** The org layer (spec §6.6): only an org admin can lift it. */
53
+ export const orgLayerNote = (hosts) => one(hosts)
54
+ ? `[network] ${list(hosts)} is not in the organization's network allowlist. Only an organization admin can add it; the user can ask an admin from the conversation. Continue without it or tell the user.`
55
+ : `[network] ${list(hosts)} are not in the organization's network allowlist. Only an organization admin can add them; the user can ask an admin from the conversation. Continue without them or tell the user.`;
56
+ /** The address layer (spec §6.6): nobody can lift it. */
57
+ export const addressLayerNote = (hosts) => one(hosts)
58
+ ? `[network] ${list(hosts)} is a private or local address, which commands can never reach.`
59
+ : `[network] ${list(hosts)} are private or local addresses, which commands can never reach.`;
60
+ /**
61
+ * An unattended run (spec §8.1): the task approves the host, yet the device refused it: the device
62
+ * takes no network grants from the deployment (`remoteGrants` off, or a program too old for them).
63
+ * Not asked, not a missing grant: only the device's owner can allow it locally.
64
+ */
65
+ export const taskGrantIgnoredNote = (hosts) => `${blockedLead(hosts)} The scheduled task approves ${pron(hosts)}, but this device does not accept the task's network grants. The device owner can allow ${pron(hosts)} locally: ${localAdd(hosts)}. Continue without ${pron(hosts)}.`;
66
+ /** An unattended run in `fail` mode (spec §8.3): no ask, the creator can add a task grant. */
67
+ export const unattendedNetworkNote = (hosts) => `[network] ${list(hosts)} ${one(hosts) ? "is" : "are"} blocked on this device and this scheduled run cannot ask for network access. The task's owner can add ${pron(hosts)} to the task's approved domains. Continue without ${pron(hosts)}.`;
68
+ /** Hosts in first-seen order, deduplicated, lower-cased, trailing dot dropped. */
69
+ function hostsOf(blocked) {
70
+ const seen = new Set();
71
+ const out = [];
72
+ for (const b of blocked) {
73
+ const host = b.host.toLowerCase().replace(/\.$/, "");
74
+ if (host === "" || host.length > NETWORK_HOST_MAX_CHARS || seen.has(host))
75
+ continue;
76
+ seen.add(host);
77
+ out.push({ ...b, host });
78
+ }
79
+ return out;
80
+ }
81
+ /** The hosts of a `network_blocked` event: valid, deduplicated, at most {@link NETWORK_ASK_MAX_HOSTS}. */
82
+ export function blockedEventHosts(blocked) {
83
+ return hostsOf(blocked)
84
+ .filter((b) => Number.isInteger(b.port) && b.port >= 1 && b.port <= 65535)
85
+ .slice(0, NETWORK_ASK_MAX_HOSTS);
86
+ }
87
+ /**
88
+ * Decides what a command's refused hosts become (spec §6.2, §6.8, §6.9, §8.3), given the run
89
+ * tree's network asks (`run`). The caller asks only for foreground `Bash` on a device; this decides
90
+ * the rest:
91
+ * - `address` and `org` layers: a note each (in an unattended run an `org` host is a missing grant);
92
+ * `unknown`: nothing (the device's trailer explains);
93
+ * - `device` layer on a program without `policyOp`: nothing (no ask, the trailer explains);
94
+ * - approved by the scheduled task the run serves (`run.taskGranted`) yet refused: a note that
95
+ * the device ignores the task's grants (never asked, never a missing grant);
96
+ * - refused earlier in the run tree: the denied note; an unattended run in `fail` mode: its note
97
+ * and a missing grant;
98
+ * - the rest is asked, unless another ask of the run tree is pending or the run made
99
+ * {@link MAX_NETWORK_ASKS_PER_RUN} asks (a note each).
100
+ */
101
+ export function planNetworkAsk(blocked, run, ctx) {
102
+ const address = [];
103
+ const org = [];
104
+ const refused = [];
105
+ const localOnly = [];
106
+ const unattended = [];
107
+ const taskIgnored = [];
108
+ const candidates = [];
109
+ const missingGrants = [];
110
+ // a machine that reports blocked hosts and takes policy changes (Docker mode has no report)
111
+ const canAsk = ctx.device?.features.includes("networkReport") === true &&
112
+ ctx.device.features.includes("policyOp");
113
+ // no run grant or policy change reaches a device whose owner turned remote grants off
114
+ const remote = ctx.device?.policy?.remoteGrants !== "off";
115
+ for (const b of hostsOf(blocked)) {
116
+ if (b.layer === "address")
117
+ address.push(b.host);
118
+ else if (b.layer === "org") {
119
+ org.push(b.host);
120
+ if (unattendedRules(ctx.unattended))
121
+ missingGrants.push(networkGrantKey(b.host));
122
+ }
123
+ else if (b.layer === "device") {
124
+ if (!canAsk)
125
+ continue;
126
+ // relayer already made an invalid name an address; a wildcard is never asked
127
+ if (b.host.startsWith("*.") || validateAllowHost(b.host).length > 0) {
128
+ address.push(b.host);
129
+ }
130
+ else if (run?.taskGranted?.has(b.host))
131
+ taskIgnored.push(b.host);
132
+ else if (run?.refused.has(b.host))
133
+ refused.push(b.host);
134
+ else if (unattendedFails(ctx.unattended)) {
135
+ unattended.push(b.host);
136
+ missingGrants.push(networkGrantKey(b.host));
137
+ }
138
+ else if (!remote)
139
+ localOnly.push(b.host);
140
+ else
141
+ candidates.push({ host: b.host, port: b.port });
142
+ }
143
+ }
144
+ const notes = [];
145
+ if (refused.length > 0)
146
+ notes.push(deniedNote(refused));
147
+ if (localOnly.length > 0)
148
+ notes.push(localOnlyNote(localOnly));
149
+ if (taskIgnored.length > 0)
150
+ notes.push(taskGrantIgnoredNote(taskIgnored));
151
+ if (unattended.length > 0)
152
+ notes.push(unattendedNetworkNote(unattended));
153
+ let ask = [];
154
+ if (candidates.length > 0) {
155
+ const names = candidates.map((c) => c.host);
156
+ if (run?.pending === true)
157
+ notes.push(otherAskPendingNote(names));
158
+ else if ((run?.asks ?? 0) >= MAX_NETWORK_ASKS_PER_RUN)
159
+ notes.push(tooManyNote(names));
160
+ else
161
+ ask = candidates.slice(0, NETWORK_ASK_MAX_HOSTS);
162
+ }
163
+ if (org.length > 0)
164
+ notes.push(orgLayerNote(org));
165
+ if (address.length > 0)
166
+ notes.push(addressLayerNote(address));
167
+ return { ask, scopes: ["once", "always"], notes, missingGrants };
168
+ }
169
+ /**
170
+ * The note of a settled network ask (spec §6.6): one line per outcome. An approval allows
171
+ * `detail.grantedHosts` (once, always, or once after 「一直允许」 fell back) and declines the hosts
172
+ * left unchecked; `hours` is the ask's own wait (created to expiry). Null while it is pending.
173
+ */
174
+ export function settledNetworkNotes(a, hours) {
175
+ const asked = (a.detail?.hosts ?? []).map((h) => h.host);
176
+ switch (a.status) {
177
+ case "approved": {
178
+ // 「一直允许」 the device refused: nothing took effect (compared as a string: the outcome
179
+ // may predate this build's type)
180
+ if (a.detail?.outcome === "refused") {
181
+ return [alwaysRefusedNote(asked)];
182
+ }
183
+ const granted = a.detail?.grantedHosts ?? asked;
184
+ const rest = asked.filter((h) => !granted.includes(h));
185
+ const notes = [];
186
+ if (granted.length > 0) {
187
+ notes.push(a.detail?.outcome === "always" ? allowedAlwaysNote(granted) : allowedOnceNote(granted));
188
+ }
189
+ if (rest.length > 0)
190
+ notes.push(deniedNote(rest));
191
+ return notes;
192
+ }
193
+ case "denied":
194
+ return [deniedNote(asked, a.denyReason)];
195
+ case "expired":
196
+ return [expiredNote(asked, hours)];
197
+ case "skipped":
198
+ return [skippedNote(asked)];
199
+ case "cancelled":
200
+ return [cancelledNote()];
201
+ default:
202
+ return null;
203
+ }
204
+ }
205
+ /** `text` followed by `notes` after a blank line (unchanged without notes). */
206
+ export const withNetworkNotes = (text, notes) => notes.length === 0 ? text : `${text}\n\n${notes.join("\n")}`;
207
+ /**
208
+ * A kept result's content within {@link NETWORK_ASK_KEPT_MAX_BYTES} (UTF-8): the head, then
209
+ * {@link NETWORK_ASK_CLIPPED_NOTE}. The caller archives large output first, so this rarely cuts.
210
+ */
211
+ export function clipKept(content) {
212
+ const bytes = Buffer.from(content, "utf8");
213
+ if (bytes.length <= NETWORK_ASK_KEPT_MAX_BYTES)
214
+ return content;
215
+ const room = NETWORK_ASK_KEPT_MAX_BYTES - Buffer.byteLength(NETWORK_ASK_CLIPPED_NOTE) - 1;
216
+ // a cut inside a multi-byte character drops it rather than keeping half of it
217
+ const head = new TextDecoder("utf-8", { fatal: false })
218
+ .decode(bytes.subarray(0, room))
219
+ .replace(/�$/, "");
220
+ return `${head}\n${NETWORK_ASK_CLIPPED_NOTE}`;
221
+ }
@@ -7,18 +7,22 @@ import { ExtensionHost } from "../extensions/host.js";
7
7
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
8
8
  import { KernelDb } from "../infra/database/kernel-db.js";
9
9
  import { TransactionHost } from "../infra/database/transaction-host.js";
10
+ import { DevicePolicyService } from "../local-devices/device-policy.service.js";
11
+ import { type NetworkRunState } from "../local-devices/network-grants.js";
10
12
  import type { OrgRole } from "../orgs/roles.js";
11
13
  import { type ConversationKindForHooks, type HookRunner } from "../runtime/hook-calls.js";
12
14
  import { type SubagentParent, type WaitingFor } from "../runtime/loop.js";
13
15
  import type { DispatchArgs, DispatchOutcome } from "../runtime/subagents/dispatch.js";
14
16
  import { SubagentMessages } from "../runtime/subagents/messages.js";
15
17
  import { SubagentsService } from "../runtime/subagents/subagents.service.js";
18
+ import { type UnattendedRun } from "../schedules/unattended.js";
16
19
  import { ApprovalsService } from "./approvals.service.js";
17
20
  import { Breaker } from "./breaker.js";
18
21
  import { Credentials } from "./credentials.js";
19
22
  import { ToolDrift } from "./drift.js";
20
23
  import { OrgToolSwitches } from "./org-tool-switches.js";
21
24
  import { ToolOutputs } from "./outputs.js";
25
+ import { type GrantKind } from "./policy.js";
22
26
  import { QuestionsService } from "./questions.service.js";
23
27
  import { type CallPolicy, type ResolvedTool, ToolRegistry } from "./registry.js";
24
28
  /** 审批被拒绝。 */
@@ -84,7 +88,15 @@ export type PolicyDecision = "allow" | "deny" | {
84
88
  }
85
89
  /** 记住的授权把 `ask` 放行(按 allow 执行,审计记授权种类)。 */
86
90
  | {
87
- grant: "session" | "always";
91
+ grant: GrantKind;
92
+ }
93
+ /**
94
+ * Refused without an approval: an unattended run in `fail` mode (scheduled tasks spec §7). The
95
+ * text is the tool error; `missingGrant` is added to the task run's `missing_grants`.
96
+ */
97
+ | {
98
+ refused: string;
99
+ missingGrant?: string;
88
100
  };
89
101
  export interface PolicyContext {
90
102
  orgId: string;
@@ -114,6 +126,11 @@ export interface PolicyContext {
114
126
  * null / unset: policies, grants and approvals use the tool key.
115
127
  */
116
128
  callPolicy?: CallPolicy | null;
129
+ /**
130
+ * The scheduled task run the run serves, unattended or trial (scheduled tasks spec §7); unset or
131
+ * null: an attended run.
132
+ */
133
+ unattended?: UnattendedRun | null;
117
134
  }
118
135
  /** 管线第 3 阶段:策略判定。 */
119
136
  export type DecidePolicy = (tool: ResolvedTool, agentDef: AgentDefinition, ctx: PolicyContext) => Promise<PolicyDecision>;
@@ -138,8 +155,8 @@ interface PipelineDeps {
138
155
  readonly credentials: Pick<Credentials, "injection" | "onUnauthorized">;
139
156
  readonly breaker: Pick<Breaker, "check" | "failure" | "success">;
140
157
  readonly outputs: Pick<ToolOutputs, "save" | "load">;
141
- /** 策略为 `ask` 时建审批;续跑时读该调用的审批结果。 */
142
- readonly approvals: Pick<ApprovalsService, "request" | "forCall">;
158
+ /** 策略为 `ask` 时建审批;续跑时读该调用的审批结果。 A refused network host: a network ask. */
159
+ readonly approvals: Pick<ApprovalsService, "request" | "forCall" | "networkForCall" | "requestNetwork">;
143
160
  /** 处理函数返回 `input_required` 时建输入请求;续跑时读该调用的各轮输入请求。 */
144
161
  readonly questions: Pick<QuestionsService, "ask" | "forCall">;
145
162
  readonly log: Logger;
@@ -180,6 +197,18 @@ interface PipelineDeps {
180
197
  * it, their calls reach the builtins' guards, which refuse them.
181
198
  */
182
199
  readonly messages: Pick<SubagentMessages, "toChild" | "toParent"> | undefined;
200
+ /**
201
+ * What a command's refused network hosts need (network ask-to-allow spec §6.2): the run tree's
202
+ * network asks and the device's features and policy summary. Without them (hand-built test
203
+ * pipelines) a report only writes `network_blocked`.
204
+ */
205
+ readonly network: {
206
+ /** `deviceId` scopes the run tree's asks to the device (ignored by an older provider). */
207
+ grants: {
208
+ runState(runId: string, deviceId?: string): Promise<NetworkRunState>;
209
+ };
210
+ devices: Pick<DevicePolicyService, "summary">;
211
+ } | undefined;
183
212
  }
184
213
  /** The sub-agent dispatch the pipeline calls (`SubagentsService.dispatch`). */
185
214
  export type SubagentDispatch = NonNullable<PipelineDeps["subagents"]>["dispatch"];
@@ -206,6 +235,11 @@ export interface ToolStepContext {
206
235
  * (spec §10.1, agent map spec §6), and it dispatches sub-agents only below `subagents.maxDepth`.
207
236
  */
208
237
  parent?: SubagentParent;
238
+ /**
239
+ * The scheduled task run this run serves (scheduled tasks spec §7); filled by `runStep` once per
240
+ * step from PG (`unattendedRunOf`), null when attended. Callers leave it unset.
241
+ */
242
+ unattended?: UnattendedRun | null;
209
243
  }
210
244
  /** tools 节点一步的结果:全部调用都有了结果,或某个调用需要等人处理(之后的调用留到续跑)。 */
211
245
  export type ToolStepOutcome = {
@@ -242,6 +276,7 @@ export type ExecOutcome =
242
276
  kind: "connection_required";
243
277
  connectorId: string;
244
278
  connectorName: string;
279
+ connectorSlug: string;
245
280
  message: string;
246
281
  };
247
282
  /**
@@ -260,9 +295,11 @@ export declare class ToolPipeline {
260
295
  /** The `Agent` tool's sub-agent dispatch; absent (tests): `Agent` calls are refused. */
261
296
  subagents: Pick<SubagentsService, "dispatch"> | undefined,
262
297
  /** The message tools between agents; absent (tests): their calls are refused. */
263
- messages: Pick<SubagentMessages, "toChild" | "toParent"> | undefined, registry: Pick<ToolRegistry, "forAgent" | "findConnector" | "handler">, credentials: Pick<Credentials, "injection" | "onUnauthorized">, breaker: Pick<Breaker, "check" | "failure" | "success">, outputs: Pick<ToolOutputs, "save" | "load">, approvals: Pick<ApprovalsService, "request" | "forCall">, questions: Pick<QuestionsService, "ask" | "forCall">, config: Pick<KernelConfigService, "get">, log: Logger,
298
+ messages: Pick<SubagentMessages, "toChild" | "toParent"> | undefined, registry: Pick<ToolRegistry, "forAgent" | "findConnector" | "handler">, credentials: Pick<Credentials, "injection" | "onUnauthorized">, breaker: Pick<Breaker, "check" | "failure" | "success">, outputs: Pick<ToolOutputs, "save" | "load">, approvals: Pick<ApprovalsService, "request" | "forCall" | "networkForCall" | "requestNetwork">, questions: Pick<QuestionsService, "ask" | "forCall">, config: Pick<KernelConfigService, "get">, log: Logger,
264
299
  /** Plugin hooks and inline ui contributions; absent (tests): no hooks, cards dropped. */
265
- host: Pick<ExtensionHost, "hooks" | "current"> | undefined, drift: Pick<ToolDrift, "onToolsChanged"> | undefined, effective: Pick<EffectiveConfigs, "get">, switches: Pick<OrgToolSwitches, "isToolOff">, txHost: TransactionHost, hooks?: ToolPipelineHooks);
300
+ host: Pick<ExtensionHost, "hooks" | "current"> | undefined, drift: Pick<ToolDrift, "onToolsChanged"> | undefined, effective: Pick<EffectiveConfigs, "get">, switches: Pick<OrgToolSwitches, "isToolOff">, txHost: TransactionHost, hooks?: ToolPipelineHooks, networkGrants?: {
301
+ runState(runId: string, deviceId?: string): Promise<NetworkRunState>;
302
+ }, devicePolicy?: Pick<DevicePolicyService, "summary">);
266
303
  /**
267
304
  * tools 节点:按顺序处理本步 assistant 消息里的全部 `tool_call`,每个调用都以一条 `tool_result` 结束
268
305
  * (之后才回到 prepare 构建下一次请求,保持「已记录调用、未记录结果之间不构建请求」的不变式)。
@@ -270,7 +307,7 @@ export declare class ToolPipeline {
270
307
  * 某个调用需要等人处理(审批 / 用户输入)→ 返回 `suspended`,之后的调用留到续跑时处理;
271
308
  * 续跑时重新进入本函数,按 PG 里的审批 / 输入请求结果继续。
272
309
  */
273
- runStep(ctx: ToolStepContext): Promise<ToolStepOutcome>;
310
+ runStep(given: ToolStepContext): Promise<ToolStepOutcome>;
274
311
  /**
275
312
  * Writes, in the caller's transaction (`mandatory`), what a run that is being stopped (a person,
276
313
  * its deadline, a failure, the wait sweep) still owes its log: the report of its fork invocation
@@ -292,6 +329,16 @@ export declare class ToolPipeline {
292
329
  */
293
330
  private processCall;
294
331
  private runCall;
332
+ /**
333
+ * A foreground command's refused network hosts (network ask-to-allow spec §6.2, §6.8, §8.3):
334
+ * the `network_blocked` event, the missing grants of an unattended run, and either notes for the
335
+ * result written now, or (device-layer hosts worth asking) `network_blocked` and the call's
336
+ * `tool.called` audit written now, the result kept in a network ask to the device's owner and
337
+ * the run's wait. The kept result is the formatted one (≤ 16 KB: larger output is archived)
338
+ * followed by the notes written now; it is written with the decision's note when the ask
339
+ * settles (P-D2).
340
+ */
341
+ private networkBlocked;
295
342
  /** Appends a sub-agent's connect card to its root conversation ({@link rootSurfaceOf}). */
296
343
  private surfaceConnectInRoot;
297
344
  /**