@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
@@ -1,4 +1,5 @@
1
- import { componentOn } from "../catalog/component-state.js";
1
+ import { skillDisabled } from "../agents/definition.js";
2
+ import { componentOn, platformBuiltin } from "../catalog/component-state.js";
2
3
  /** The `source` prefix of a platform skill version in a snapshot or an invocation record. */
3
4
  export const PLATFORM_SOURCE = "platform:";
4
5
  /** The snapshot `source` of a platform skill version stored at `prefix` (in `deployment/`). */
@@ -38,6 +39,9 @@ async function platformRowsOn(db, orgId, ids) {
38
39
  "c.id as item_id",
39
40
  "c.disabled as item_disabled",
40
41
  "c.builtin as item_builtin",
42
+ "c.owner_org_id as item_owner",
43
+ "p.owner_org_id",
44
+ "p.shareable",
41
45
  eb
42
46
  .exists(eb
43
47
  .selectFrom("plugin_installs as a")
@@ -66,18 +70,28 @@ async function platformRowsOn(db, orgId, ids) {
66
70
  .where("p.disabled", "=", false);
67
71
  if (ids !== undefined)
68
72
  q = q.where("p.id", "in", [...ids]);
73
+ // the share gate (ruling P3-1, as `usableBy` in catalog/ownership.ts): another org's skill
74
+ // while its owner shares it, or while this org is associated with it (listed, its plugin
75
+ // installed here, or switched on here: an org keeps what it has when sharing stops, P3-16)
76
+ const gate = (r) => r.owner_org_id === null ||
77
+ r.owner_org_id === orgId ||
78
+ r.shareable ||
79
+ Boolean(r.listed) ||
80
+ Boolean(r.installed) ||
81
+ r.switch === true;
69
82
  // the predicate of component scope (catalog/component-state.ts); `disabled` is filtered above
70
- return (await q.execute()).filter((r) => componentOn({ scopeMode: r.scope_mode, scope: r.scope, disabled: false, itemId: r.catalog_item_id }, r.catalog_item_id === null
71
- ? null
72
- : {
73
- live: r.item_id !== null,
74
- disabled: r.item_disabled === true,
75
- builtin: r.item_builtin === true,
76
- // the plugin's own scope and list do not decide whether a component is on
77
- scope: "optional",
78
- listed: false,
79
- installed: Boolean(r.installed),
80
- }, Boolean(r.listed), r.switch ?? null));
83
+ return (await q.execute()).filter((r) => gate(r) &&
84
+ componentOn({ scopeMode: r.scope_mode, scope: r.scope, disabled: false, itemId: r.catalog_item_id }, r.catalog_item_id === null
85
+ ? null
86
+ : {
87
+ live: r.item_id !== null,
88
+ disabled: r.item_disabled === true,
89
+ builtin: platformBuiltin({ builtin: r.item_builtin, owner_org_id: r.item_owner }),
90
+ // the plugin's own scope and list do not decide whether a component is on
91
+ scope: "optional",
92
+ listed: false,
93
+ installed: Boolean(r.installed),
94
+ }, Boolean(r.listed), r.switch ?? null));
81
95
  }
82
96
  /**
83
97
  * The ids among `ids` of the platform skills on in the org (see {@link platformOn}), whatever the
@@ -114,7 +128,9 @@ export async function pinnedSkillLinksServed(db, orgId, ids) {
114
128
  * - `own`: `wantedOn` over the skill's own scope, the org's list membership and its switch.
115
129
  * One query for the skills with their plugin and org state, one for the org's names.
116
130
  */
117
- async function platformOn(db, orgId, ids, log) {
131
+ async function platformOn(db, orgId, ids, log,
132
+ /** Ids nobody selected (builtin skills): their shadowing is logged at `debug`. */
133
+ quiet = new Set()) {
118
134
  const rows = await platformRowsOn(db, orgId, ids);
119
135
  if (rows.length === 0)
120
136
  return [];
@@ -143,12 +159,12 @@ async function platformOn(db, orgId, ids, log) {
143
159
  const sameComponent = o.platform_skill_id === r.id ||
144
160
  (o.catalog_item_id !== null && o.catalog_item_id === r.catalog_item_id);
145
161
  if (!sameComponent) {
146
- log?.warn("platform skill shadowed by an org skill of the same name", {
147
- orgId,
148
- name: r.name,
149
- platformSkillId: r.id,
150
- skillId: o.id,
151
- });
162
+ const fields = { orgId, name: r.name, platformSkillId: r.id, skillId: o.id };
163
+ const msg = "platform skill shadowed by an org skill of the same name";
164
+ if (quiet.has(r.id))
165
+ log?.debug?.(msg, fields);
166
+ else
167
+ log?.warn(msg, fields);
152
168
  }
153
169
  continue;
154
170
  }
@@ -187,7 +203,9 @@ async function platformOn(db, orgId, ids, log) {
187
203
  * while the platform skill is on in the org; a pinned copy as its own version, while its platform
188
204
  * skill is on in the org (ruling P2-18); a platform skill id while it is on in the org.
189
205
  */
190
- export async function skillVersionsById(db, orgId, ids, log) {
206
+ export async function skillVersionsById(db, orgId, ids, log,
207
+ /** Ids nobody selected (builtin skills): a name clash of theirs is logged at `debug`. */
208
+ quiet = new Set()) {
191
209
  const unique = [...new Set(ids)];
192
210
  const out = new Map();
193
211
  if (unique.length === 0)
@@ -222,7 +240,7 @@ export async function skillVersionsById(db, orgId, ids, log) {
222
240
  ...linked.map((r) => r.platform_skill_id),
223
241
  ]),
224
242
  ];
225
- const platform = new Map((await platformOn(db, orgId, platformIds, log)).map((p) => [p.id, p]));
243
+ const platform = new Map((await platformOn(db, orgId, platformIds, log, quiet)).map((p) => [p.id, p]));
226
244
  const owned = await ownedPlatformIds(db, orgId, linked.map((r) => r.platform_skill_id));
227
245
  // ruling P2-18: a pinned copy serves its own version while its platform skill is on in the org
228
246
  const pinnedOn = await pinnedSkillLinksServed(db, orgId, pinned.map((r) => r.platform_skill_id));
@@ -285,33 +303,149 @@ async function ownedPlatformIds(db, orgId, platformIds) {
285
303
  return new Set(rows.map((r) => r.id));
286
304
  }
287
305
  /**
288
- * The skills an agent selects, sorted by id: its own skills (`scope = 'agent'`), the org skills
289
- * it shared (`origin_agent_id`: the agent keeps a shared skill without a definition change) and
290
- * the ids of `orgSkills` that resolve ({@link skillVersionsById}: org skills, linked copies,
291
- * platform skills on in the org). Not deduplicated by name.
306
+ * The skill ids no agent can turn off (skill authoring spec §5.7, skills default-on spec §3): the
307
+ * live platform skills of the platform's builtin plugins (`catalog_items.builtin`, platform-owned),
308
+ * plus the org's pinned copies of them (a pinned copy shadows its platform skill by name, ruling
309
+ * P2-18). Whether each is on in the org (a component switch, the deployment's disable) is left to
310
+ * the resolvers.
292
311
  */
293
- export async function selectedSkills(db, a, log) {
294
- const agentOwn = await db
312
+ export async function builtinSkillIds(db, orgId) {
313
+ const platform = await db
314
+ .selectFrom("platform_skills as p")
315
+ .innerJoin("catalog_items as c", "c.id", "p.catalog_item_id")
316
+ .select("p.id")
317
+ .where("c.builtin", "=", true)
318
+ .where("c.owner_org_id", "is", null)
319
+ .where("c.deleted_at", "is", null)
320
+ .where("p.owner_org_id", "is", null)
321
+ .where("p.deleted_at", "is", null)
322
+ .where("p.disabled", "=", false)
323
+ .execute();
324
+ if (platform.length === 0)
325
+ return [];
326
+ const ids = platform.map((r) => r.id);
327
+ const pinnedCopies = await db
295
328
  .selectFrom("skills")
296
329
  .select("id")
297
- .where("org_id", "=", a.orgId)
298
- .where("scope", "=", "agent")
299
- .where("agent_id", "=", a.agentId)
330
+ .where("org_id", "=", orgId)
331
+ .where("scope", "=", "org")
332
+ .where("deleted_at", "is", null)
333
+ .where("pinned_version", "is not", null)
334
+ .where("platform_skill_id", "in", ids)
335
+ .execute();
336
+ return [...ids, ...pinnedCopies.map((r) => r.id)];
337
+ }
338
+ /**
339
+ * The names of the platform's builtin plugins (`catalog_items.builtin`, platform-owned, live): their
340
+ * skills are always on, and an agent cannot turn them off (skills default-on spec §3).
341
+ */
342
+ export async function builtinSkillPlugins(db) {
343
+ const rows = await db
344
+ .selectFrom("catalog_items")
345
+ .select("name")
346
+ .where("builtin", "=", true)
347
+ .where("owner_org_id", "is", null)
300
348
  .where("deleted_at", "is", null)
301
349
  .execute();
302
- const shared = await db
350
+ return new Set(rows.map((r) => r.name));
351
+ }
352
+ /**
353
+ * The ids of the skills an agent always has, whatever its `disabledSkills` say: its own skills
354
+ * (`scope = 'agent'`) and the org skills it shared (`origin_agent_id`: the agent keeps a shared
355
+ * skill without a definition entry).
356
+ */
357
+ export async function agentOwnSkillIds(db, a) {
358
+ const rows = await db
303
359
  .selectFrom("skills")
304
- .select("id")
360
+ .select(["id", "scope"])
305
361
  .where("org_id", "=", a.orgId)
362
+ .where("deleted_at", "is", null)
363
+ .where((eb) => eb.or([
364
+ eb.and([eb("scope", "=", "agent"), eb("agent_id", "=", a.agentId)]),
365
+ eb.and([eb("scope", "=", "org"), eb("origin_agent_id", "=", a.agentId)]),
366
+ ]))
367
+ .execute();
368
+ return {
369
+ own: rows.filter((r) => r.scope === "agent").map((r) => r.id),
370
+ shared: rows.filter((r) => r.scope === "org").map((r) => r.id),
371
+ };
372
+ }
373
+ /**
374
+ * The org's copies linked to the platform skills `platformIds` without a pin (ruling B2), by
375
+ * platform skill id (the first by id when there are several): such a copy keeps its own id in an
376
+ * agent's catalog, as when it was selected, so the snapshot does not change when a copy gets
377
+ * linked.
378
+ */
379
+ export async function linkedCopyIds(db, orgId, platformIds) {
380
+ const linked = new Map();
381
+ if (platformIds.length === 0)
382
+ return linked;
383
+ const copies = await db
384
+ .selectFrom("skills")
385
+ .select(["id", "platform_skill_id"])
386
+ .where("org_id", "=", orgId)
306
387
  .where("scope", "=", "org")
307
- .where("origin_agent_id", "=", a.agentId)
308
388
  .where("deleted_at", "is", null)
389
+ .where("pinned_version", "is", null)
390
+ .where("platform_skill_id", "in", [...platformIds])
391
+ .orderBy("id")
309
392
  .execute();
310
- const orgSkills = new Set([...a.orgSkills, ...shared.map((r) => r.id)]);
311
- const byId = await skillVersionsById(db, a.orgId, [...agentOwn.map((r) => r.id), ...orgSkills], log);
312
- return [...byId.values()]
313
- .filter((r) => (r.scope === "agent" ? r.agentId === a.agentId : orgSkills.has(r.id)))
314
- .sort((x, y) => (x.id < y.id ? -1 : x.id > y.id ? 1 : 0));
393
+ for (const c of copies) {
394
+ const pid = c.platform_skill_id;
395
+ if (!linked.has(pid))
396
+ linked.set(pid, c.id);
397
+ }
398
+ return linked;
399
+ }
400
+ /**
401
+ * The ids a disabled skill entry may name for `r`: its own, the platform skill it serves and the
402
+ * org's linked copy listed under it (`linked`, from {@link linkedCopyIds}).
403
+ */
404
+ export function skillIdsOf(r, linked) {
405
+ const ids = new Set([r.id]);
406
+ if (r.platformSkillId !== undefined)
407
+ ids.add(r.platformSkillId);
408
+ const copy = linked.get(r.id);
409
+ if (copy !== undefined)
410
+ ids.add(copy);
411
+ return [...ids];
412
+ }
413
+ /**
414
+ * The skills an agent has (skills default-on spec §3), sorted by id: its own skills, the org
415
+ * skills it shared, and every skill on in the org ({@link usableSkillVersions}) minus what
416
+ * `disabledSkills` turns off. A plugin entry matches a skill's `plugin` and name, a skill entry its
417
+ * id (or the platform skill a copy serves). The skills of builtin plugins, the agent's own and its
418
+ * shared ones are never turned off. Not deduplicated by name (`resolveSkills` ranks).
419
+ */
420
+ export async function agentSkills(db, a, log) {
421
+ const { own, shared } = await agentOwnSkillIds(db, a);
422
+ const always = await skillVersionsById(db, a.orgId, [...own, ...shared], log);
423
+ const out = new Map();
424
+ for (const r of always.values()) {
425
+ if (r.scope === "agent" ? r.agentId === a.agentId : true)
426
+ out.set(r.id, r);
427
+ }
428
+ const usable = await usableSkillVersions(db, a.orgId, log);
429
+ // a linked copy keeps its own id in the catalog (see linkedCopyIds)
430
+ const linked = await linkedCopyIds(db, a.orgId, usable.flatMap((r) => (r.origin === "platform" ? [r.id] : [])));
431
+ const builtin = a.disabledSkills.length === 0 ? new Set() : await builtinSkillPlugins(db);
432
+ const builtinIds = a.disabledSkills.length === 0 ? new Set() : new Set(await builtinSkillIds(db, a.orgId));
433
+ for (const r of usable) {
434
+ if (out.has(r.id))
435
+ continue;
436
+ const ids = skillIdsOf(r, linked);
437
+ const fixed = (r.plugin !== null && builtin.has(r.plugin) && r.origin === "platform") ||
438
+ ids.some((id) => builtinIds.has(id));
439
+ if (!fixed && skillDisabled(a.disabledSkills, { plugin: r.plugin, name: r.name, ids })) {
440
+ continue;
441
+ }
442
+ const copy = linked.get(r.id);
443
+ if (copy === undefined)
444
+ out.set(r.id, r);
445
+ else if (!out.has(copy))
446
+ out.set(copy, { ...r, id: copy });
447
+ }
448
+ return [...out.values()].sort((x, y) => (x.id < y.id ? -1 : x.id > y.id ? 1 : 0));
315
449
  }
316
450
  /**
317
451
  * The org's usable skills, sorted by name: its own live org skills (`scope = 'org'`, unlinked, or
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { type Principal } from "../identity/principal.js";
3
3
  import { type OrgContext } from "../orgs/org.guard.js";
4
- import { type ApprovalRecord, ApprovalsService, type ApprovalView, type ToolGrantView } from "./approvals.service.js";
4
+ import { type ApprovalPublicRecord, ApprovalsService, type ApprovalView, type ToolGrantView } from "./approvals.service.js";
5
5
  declare const ListQuery: z.ZodObject<{
6
6
  status: z.ZodOptional<z.ZodEnum<{
7
7
  approved: "approved";
@@ -9,6 +9,7 @@ declare const ListQuery: z.ZodObject<{
9
9
  denied: "denied";
10
10
  expired: "expired";
11
11
  pending: "pending";
12
+ skipped: "skipped";
12
13
  }>>;
13
14
  }, z.core.$strict>;
14
15
  declare const DecisionBody: z.ZodObject<{
@@ -23,6 +24,7 @@ declare const DecisionBody: z.ZodObject<{
23
24
  }>>;
24
25
  note: z.ZodOptional<z.ZodString>;
25
26
  reason: z.ZodOptional<z.ZodString>;
27
+ hosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
28
  }, z.core.$strict>;
27
29
  /**
28
30
  * 工具调用审批(`/api/v1/orgs/:orgId/approvals`,`approvals.decide`:访客起、仅限用户主体——agent 不能审批,R2)。
@@ -38,7 +40,7 @@ export declare class ApprovalsController {
38
40
  approval: ApprovalView;
39
41
  }>;
40
42
  decide(principal: Principal, org: OrgContext, id: string, body: z.infer<typeof DecisionBody>): Promise<{
41
- approval: ApprovalRecord;
43
+ approval: ApprovalPublicRecord;
42
44
  }>;
43
45
  }
44
46
  /** 本人的「以后都允许」授权(`/api/v1/orgs/:orgId/me/tool-grants`):查看与撤销,只动自己的。 */
@@ -9,13 +9,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
9
9
  };
10
10
  import { Controller, Delete, Get, HttpCode, Inject, Param, Post } from "@nestjs/common";
11
11
  import { z } from "zod";
12
+ import { NETWORK_ASK_MAX_HOSTS, NETWORK_HOST_MAX_CHARS } from "../conversations/events.js";
12
13
  import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
13
14
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
14
15
  import { ZBody, ZQuery } from "../infra/validation/zod-params.js";
15
16
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
16
17
  import { CONTROL_OR_HIDDEN_RE } from "../text.js";
17
18
  import { ApprovalsListQuerySchema } from "./approvals.schemas.js";
18
- import { APPROVAL_DENY_REASON_MAX_CHARS, ApprovalsService, } from "./approvals.service.js";
19
+ import { APPROVAL_DENY_REASON_MAX_CHARS, ApprovalsService, publicRecord, } from "./approvals.service.js";
19
20
  const ListQuery = ApprovalsListQuerySchema;
20
21
  const DecisionBody = z
21
22
  .object({
@@ -32,11 +33,24 @@ const DecisionBody = z
32
33
  .max(APPROVAL_DENY_REASON_MAX_CHARS)
33
34
  .refine((r) => !CONTROL_OR_HIDDEN_RE.test(r.replace(/[\n\t]/g, " ")), "reason must not contain control or format characters")
34
35
  .optional(),
36
+ /**
37
+ * A network ask only: the hosts allowed, a subset of those asked (absent: all of them); the
38
+ * others count as denied.
39
+ */
40
+ hosts: z
41
+ .array(z.string().min(1).max(NETWORK_HOST_MAX_CHARS))
42
+ .min(1)
43
+ .max(NETWORK_ASK_MAX_HOSTS)
44
+ .optional(),
35
45
  })
36
46
  .strict()
37
47
  .refine((b) => b.reason === undefined || b.decision === "deny", {
38
48
  path: ["reason"],
39
49
  message: "A reason can only be given with deny",
50
+ })
51
+ .refine((b) => b.hosts === undefined || b.decision === "approve", {
52
+ path: ["hosts"],
53
+ message: "Hosts can only be given with approve",
40
54
  });
41
55
  class ApprovalsListQuery extends createZodDto(ListQuery, "ApprovalsListQuery") {
42
56
  }
@@ -61,8 +75,9 @@ let ApprovalsController = class ApprovalsController {
61
75
  }
62
76
  async decide(principal, org, id, body) {
63
77
  const p = requireUser(principal);
78
+ // a network ask's kept command result stays in the kernel
64
79
  return {
65
- approval: await this.approvals.decide(org.orgId, id, p.account.id, body, patTokenId(p)),
80
+ approval: publicRecord(await this.approvals.decide(org.orgId, id, p.account.id, body, patTokenId(p), "web")),
66
81
  };
67
82
  }
68
83
  };
@@ -8,6 +8,7 @@ export declare const ApprovalsListQuerySchema: z.ZodObject<{
8
8
  denied: "denied";
9
9
  expired: "expired";
10
10
  pending: "pending";
11
+ skipped: "skipped";
11
12
  }>>;
12
13
  }, z.core.$strict>;
13
14
  /** `:id` of `GET /approvals/:id`, as the route takes it (`@Param`). */
@@ -1,7 +1,9 @@
1
1
  import { z } from "zod";
2
2
  /** `GET /approvals?status=`: absent lists every status. */
3
3
  export const ApprovalsListQuerySchema = z
4
- .object({ status: z.enum(["pending", "approved", "denied", "expired", "cancelled"]).optional() })
4
+ .object({
5
+ status: z.enum(["pending", "approved", "denied", "expired", "cancelled", "skipped"]).optional(),
6
+ })
5
7
  .strict();
6
8
  /** `:id` of `GET /approvals/:id`, as the route takes it (`@Param`). */
7
9
  export const ApprovalPathParams = z.object({ id: z.string() });
@@ -1,16 +1,68 @@
1
- import { type LocalizedName } from "@capybara-hq/plugin-sdk";
1
+ import { type LocalizedName, type ToolDisplay } from "@capybara-hq/plugin-sdk";
2
2
  import { EventLog } from "../conversations/event-log.js";
3
+ import { type ToolView } from "../conversations/events.js";
3
4
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
4
5
  import { KernelDb } from "../infra/database/kernel-db.js";
5
6
  import { TransactionHost } from "../infra/database/transaction-host.js";
6
7
  import { KernelEvents } from "../infra/events/kernel-events.js";
8
+ import { LockService } from "../infra/lock/lock.service.js";
9
+ import { DevicePolicyService } from "../local-devices/device-policy.service.js";
7
10
  import { type OrgRole } from "../orgs/roles.js";
11
+ import { SandboxService } from "../sandbox/sandbox.service.js";
12
+ import { ScheduledTaskStore } from "../schedules/store.js";
8
13
  import type { ApprovalPolicySource } from "./pipeline.js";
14
+ import { ToolRegistry } from "./registry.js";
9
15
  export { APPROVAL_DENY_REASON_MAX_CHARS } from "../conversations/events.js";
10
16
  /** `list` 一次最多返回的审批数(收件箱据此判断这个来源是否取满)。 */
11
17
  export declare const APPROVALS_LIST_MAX = 100;
12
- export type ApprovalStatus = "pending" | "approved" | "denied" | "expired" | "cancelled";
18
+ export type ApprovalStatus = "pending" | "approved" | "denied" | "expired" | "cancelled" | "skipped";
13
19
  export type ApprovalScope = "once" | "session" | "always";
20
+ /** What an approval asks: a tool call, or the device owner about refused network hosts. */
21
+ export type ApprovalKind = "tool" | "network";
22
+ /**
23
+ * A network ask's `detail` (network ask-to-allow spec §6.1, P-D2): the device, the refused hosts,
24
+ * the command and its kept `tool_result` (written with a note when the ask settles; the command is
25
+ * never run again), and once approved what took effect.
26
+ */
27
+ export interface NetworkAskDetail {
28
+ deviceId: string;
29
+ hosts: {
30
+ host: string;
31
+ port: number;
32
+ }[];
33
+ command: string;
34
+ description: string | null;
35
+ /**
36
+ * The formatted `tool_result` of the command (≤ 16 KB; larger output already archived), and the
37
+ * display-only fields the pipeline writes with it when the ask settles.
38
+ */
39
+ result: {
40
+ content: string;
41
+ isError: boolean;
42
+ outputId?: string;
43
+ contentTypes?: string[];
44
+ display?: ToolDisplay;
45
+ view?: ToolView;
46
+ };
47
+ /** The hosts the decision allowed (every approved decision writes them; none: nothing). */
48
+ grantedHosts?: string[];
49
+ /** What took effect: a run grant, the device's policy, or a run grant because the device did not apply it. */
50
+ /**
51
+ * What took effect: a run grant (`once`), the device's policy (`always`), a run grant because
52
+ * the device could not be reached or lacks the feature (`once_fallback`), or nothing because the
53
+ * device refused the change (`refused`: its owner takes no remote grants; no run grant either).
54
+ */
55
+ outcome?: "once" | "always" | "once_fallback" | "refused";
56
+ /** Why 「一直允许」 did not reach the device's policy. */
57
+ fallback?: "offline" | "refused" | "unsupported";
58
+ }
59
+ /** A network ask's detail as the API shows it: without the kept command result. */
60
+ export type NetworkAskView = Omit<NetworkAskDetail, "result">;
61
+ /**
62
+ * The model-visible note written after a network ask's kept result when its run is stopped before
63
+ * the device owner answered (spec §6.7).
64
+ */
65
+ export declare const NETWORK_ASK_CANCELLED_NOTE = "[network] The run was stopped before the user answered; access stays blocked.";
14
66
  /** 一条审批记录。 */
15
67
  export interface ApprovalRecord {
16
68
  id: string;
@@ -20,6 +72,10 @@ export interface ApprovalRecord {
20
72
  callId: string;
21
73
  /** 同一调用的第几次审批(从 1 起;工具定义变化后重新审批时递增)。 */
22
74
  attempt: number;
75
+ /** `network`: a network ask (its tool key is `Bash`, its facts in {@link detail}). */
76
+ kind: ApprovalKind;
77
+ /** A network ask's hosts, command, kept result and outcome; null for a tool approval. */
78
+ detail: NetworkAskDetail | null;
23
79
  toolKey: string;
24
80
  specHash: string;
25
81
  connectorId: string | null;
@@ -45,8 +101,14 @@ export interface ApprovalRecord {
45
101
  expiresAt: string;
46
102
  createdAt: string;
47
103
  }
104
+ /** An approval as a route answers it: a network ask's detail without the kept command result. */
105
+ export interface ApprovalPublicRecord extends Omit<ApprovalRecord, "detail"> {
106
+ detail: NetworkAskView | null;
107
+ }
108
+ /** The record without a network ask's kept command result (what routes and cards may show). */
109
+ export declare function publicRecord(r: ApprovalRecord): ApprovalPublicRecord;
48
110
  /** 审批详情(列表与详情接口返回)。 */
49
- export interface ApprovalView extends ApprovalRecord {
111
+ export interface ApprovalView extends ApprovalPublicRecord {
50
112
  /** 来源内的工具原名(连接器工具为连接器里的名字;否则同 `toolKey`)。 */
51
113
  toolName: string;
52
114
  connector: {
@@ -108,7 +170,30 @@ export interface DecisionInput {
108
170
  note?: string | undefined;
109
171
  /** Only with `deny` (trimmed, 1–500 characters; checked by the route). */
110
172
  reason?: string | undefined;
173
+ /**
174
+ * A network ask's approval only: the hosts allowed, a subset of those asked (absent: all); the
175
+ * unchecked ones count as denied (P-D5).
176
+ */
177
+ hosts?: string[] | undefined;
178
+ }
179
+ /** Where a decision was made (the device's local audit line and `device.network_*`). */
180
+ export type DecisionVia = "web" | "im";
181
+ /** A new network ask (network ask-to-allow spec §6.2), made by the tool pipeline. */
182
+ export interface NetworkAskInput {
183
+ orgId: string;
184
+ conversationId: string;
185
+ runId: string;
186
+ /** The call's step (written to `approval_requested`). */
187
+ step: number;
188
+ callId: string;
189
+ /** The device owner: the only person who may decide. */
190
+ requestedFor: string;
191
+ detail: Omit<NetworkAskDetail, "grantedHosts" | "outcome" | "fallback">;
192
+ /** `["once", "always"]`, or `["once"]` when the device takes no remote grants. */
193
+ scopes: ("once" | "always")[];
111
194
  }
195
+ /** The tool key a network ask is recorded under (the command ran in `Bash`). */
196
+ export declare const NETWORK_ASK_TOOL_KEY = "Bash";
112
197
  /** 「以后都允许」授权(本人可撤销)。 */
113
198
  export interface ToolGrantView {
114
199
  agentId: string;
@@ -132,13 +217,39 @@ export declare class ApprovalsService {
132
217
  private readonly eventLog;
133
218
  private readonly events;
134
219
  readonly txHost: TransactionHost;
135
- constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost);
220
+ private readonly schedules;
221
+ /** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
222
+ private readonly registry;
223
+ /** 「这台设备一直允许」 sends the `policy` op to the device before the decision is recorded. */
224
+ private readonly policies;
225
+ /** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
226
+ readonly lockService: LockService;
227
+ /** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
228
+ private readonly sandbox;
229
+ constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost, schedules: Pick<ScheduledTaskStore, "addGrant">,
230
+ /** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
231
+ registry: Pick<ToolRegistry, "forAgent">,
232
+ /** 「这台设备一直允许」 sends the `policy` op to the device before the decision is recorded. */
233
+ policies: Pick<DevicePolicyService, "apply" | "summary">,
234
+ /** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
235
+ lockService: LockService,
236
+ /** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
237
+ sandbox: Pick<SandboxService, "ceilingLimits">);
136
238
  /**
137
239
  * 新建 pending 审批(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `approval_requested`,同一事务。
138
240
  * 按 (run_id, call_id, attempt) 幂等:已存在则返回已有记录(不覆盖),事件按 `approval_requested:<approvalId>` 幂等。
139
241
  */
140
242
  request(input: ApprovalRequestInput): Promise<ApprovalRecord>;
141
243
  private requestIn;
244
+ /**
245
+ * A network ask (network ask-to-allow spec §6.2): a pending approval of kind `network` for the
246
+ * device owner, expiring like a tool approval (`tools.approvalTimeoutHours`), and its
247
+ * `approval_requested` (tool key `Bash`, the display-only `network.hosts`), in one transaction.
248
+ * It takes the attempt after the call's other approvals (P-D4: the command ran, so no tool
249
+ * approval of the call follows it). One per call: a second request returns the first.
250
+ */
251
+ requestNetwork(input: NetworkAskInput): Promise<ApprovalRecord>;
252
+ private requestNetworkIn;
142
253
  /** 当前用户能处理的审批(新的在前,最多 {@link APPROVALS_LIST_MAX} 条);`status` 缺省为全部。 */
143
254
  list(orgId: string, accountId: string, status?: ApprovalStatus): Promise<ApprovalView[]>;
144
255
  /** 详情;当前用户不能处理的一律 `approval.not_found`(不泄露存在)。 */
@@ -149,15 +260,61 @@ export declare class ApprovalsService {
149
260
  * `always` → 发起人 × agent × 工具)、`approval_decided` 事件(幂等键 `approval_decided:<id>`)与审计;
150
261
  * 提交后发布事件并调用 `onDecided`。
151
262
  */
152
- decide(orgId: string, id: string, accountId: string, body: DecisionInput, actorTokenId?: string): Promise<ApprovalRecord>;
263
+ decide(orgId: string, id: string, accountId: string, body: DecisionInput, actorTokenId?: string, via?: DecisionVia): Promise<ApprovalRecord>;
264
+ /**
265
+ * A network ask's decision (network ask-to-allow spec §5.2), one at a time per ask across
266
+ * replicas: only the device owner (`requested_for`) decides, in a signed-in session (a personal
267
+ * access token is refused; the route already refuses service and embed tokens); the allowed
268
+ * hosts are a subset of those asked. 「这台设备一直允许」 sends the `policy` op first, outside any
269
+ * transaction; when the device cannot be reached or lacks the feature the decision is recorded
270
+ * as a run grant with the reason, and when the device refuses (its owner takes no remote grants)
271
+ * as `always` with outcome `refused` and nothing granted. Then {@link decideIn} records what took
272
+ * effect and the run resumes after the commit; a change the device applied after the ask closed
273
+ * is audited (`late`) and answered with a 409 that says so.
274
+ */
275
+ private decideNetwork;
276
+ /** `device.network_allowed` of a policy change that reached the device after the ask closed. */
277
+ private auditLateAllow;
278
+ /** The owner of a live device of the org (null: none, deleted, or of another org). */
279
+ private deviceOwnerOf;
280
+ /**
281
+ * Adds the hosts to the device's local policy (the `policy` op; never inside a transaction).
282
+ * Null when it was applied, else why not: the decision falls back to a run grant.
283
+ */
284
+ private applyAlways;
285
+ /** After the commit: the decision's events reach the page, the run's waits and IM delivery. */
286
+ private announceDecided;
287
+ /** An account's display name (null when unknown). */
288
+ private displayNameOf;
153
289
  private decideIn;
290
+ /**
291
+ * A trial run of a scheduled task (scheduled tasks spec §7): an approval its creator granted, in
292
+ * any scope, is also added to the task's grants with the approved definition's hash, so its
293
+ * unattended runs do not ask again. Kept only by the rule `addGrants` applies
294
+ * (`grantRefusalOf`, against the task agent's tools now): never a role-escalated approval
295
+ * (`requestedFor` null, it stays `once`), one another account decided, one whose scopes never
296
+ * allowed remembering (an org / platform operation: "once" only), a per-call key
297
+ * (`Platform:<operation>`, a skill change among them), a fixed ask, a tool the org forbids (its key or the
298
+ * call's), or a tool the task's agent does not have. In the decision's transaction.
299
+ */
300
+ private keepTrialGrant;
301
+ /**
302
+ * A trial run of a scheduled task (network ask-to-allow spec §8.1): the hosts its creator allowed
303
+ * for this run (「这次允许」) on a network ask about one of their devices become the task's
304
+ * `network:<host>` grants, each only when `networkGrantRefusal` allows it (exact host, within the
305
+ * org's effective egress, the device takes remote grants). Never for an ask another account
306
+ * decided or one asked of someone else. In the decision's transaction.
307
+ */
308
+ private keepTrialNetworkGrant;
154
309
  /**
155
310
  * 详情,不做处理人校验(内核内部用:渠道出站投递组装 IM 审批卡片);不存在返回 null。
156
311
  * 不得直接回给请求方——HTTP 接口用 {@link get}。
157
312
  */
158
313
  describe(id: string): Promise<ApprovalView | null>;
159
- /** 某个工具调用最近一次的审批(attempt 最大者);没有则返回 null。 */
314
+ /** 某个工具调用最近一次的工具审批(attempt 最大者,网络询问除外);没有则返回 null。 */
160
315
  forCall(runId: string, callId: string): Promise<ApprovalRecord | null>;
316
+ /** The network ask of a tool call (at most one per call); null when it has none. */
317
+ networkForCall(runId: string, callId: string): Promise<ApprovalRecord | null>;
161
318
  /**
162
319
  * 超时(等待机制的超时任务调用):仍 pending 且已到期 → 置 expired、写 `approval_decided{expired}`(处理人为 null)。
163
320
  * 与人工决定都锁同一行,恰好一个生效。返回 `expired`(本次生效)/ `not_due`(尚未到期)/
@@ -182,7 +339,25 @@ export declare class ApprovalsService {
182
339
  records: ApprovalRecord[];
183
340
  seqs: Map<string, number>;
184
341
  }>;
185
- /** 系统写的 `approval_decided`(超时 / 取消,处理人为 null),在当前事务内;返回事件 seq。 */
342
+ /**
343
+ * In the caller's transaction (`mandatory`; it wrote `authorAccountId`'s message in
344
+ * `conversationId` and holds that conversation's row lock): the message skips the pending
345
+ * network asks it closes (network ask-to-allow spec §6.7), as it skips questions: the asks of
346
+ * active runs `authorAccountId` started, in the conversation and in its sub-agents'
347
+ * conversations at any depth (their asks are shown in the root). Each becomes `skipped` with
348
+ * `approval_decided{skipped}` by the author; access stays blocked. Returns the skipped records and
349
+ * the highest seq written per conversation; the caller notifies and emits
350
+ * `approval.decided{skipped}` after the commit, so the runs resume.
351
+ */
352
+ skipNetworkIn(conversationId: string, authorAccountId: string): Promise<{
353
+ records: ApprovalRecord[];
354
+ seqs: Map<string, number>;
355
+ }>;
356
+ /** `conversationId` and its sub-agents' conversations at any depth (at most 16 levels). */
357
+ private subtreeOf;
358
+ /**
359
+ * 系统写的 `approval_decided`(超时 / 取消,处理人为 null;跳过,处理人为消息作者),在当前事务内;返回事件 seq。
360
+ */
186
361
  private decidedEvent;
187
362
  /** 本人在该组织的「以后都允许」授权。 */
188
363
  listGrants(orgId: string, accountId: string): Promise<ToolGrantView[]>;