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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/dist/agents/admin-agents.d.ts +16 -3
  2. package/dist/agents/admin-agents.js +65 -7
  3. package/dist/agents/agents.schemas.d.ts +24 -6
  4. package/dist/agents/agents.service.d.ts +4 -3
  5. package/dist/agents/agents.service.js +39 -26
  6. package/dist/agents/definition.d.ts +44 -6
  7. package/dist/agents/definition.js +86 -18
  8. package/dist/agents/from-template.d.ts +6 -3
  9. package/dist/agents/from-template.js +57 -44
  10. package/dist/agents/org-templates.js +97 -41
  11. package/dist/agents/templates.d.ts +6 -0
  12. package/dist/agents/templates.js +15 -1
  13. package/dist/catalog/admin-catalog.controller.d.ts +2 -4
  14. package/dist/catalog/admin-catalog.controller.js +2 -6
  15. package/dist/catalog/catalog.module.js +3 -0
  16. package/dist/catalog/claude/report.d.ts +9 -2
  17. package/dist/catalog/claude/report.js +1 -0
  18. package/dist/catalog/claude/translate.js +1 -1
  19. package/dist/catalog/component-promotion.js +1 -2
  20. package/dist/catalog/component-state.d.ts +13 -1
  21. package/dist/catalog/component-state.js +14 -5
  22. package/dist/catalog/marketplaces.service.d.ts +1 -1
  23. package/dist/catalog/marketplaces.service.js +2 -1
  24. package/dist/catalog/org-catalog.controller.d.ts +13 -3
  25. package/dist/catalog/org-catalog.controller.js +26 -4
  26. package/dist/catalog/org-catalog.schemas.d.ts +13 -0
  27. package/dist/catalog/org-catalog.schemas.js +34 -0
  28. package/dist/catalog/org-plugin-uploads.controller.js +0 -1
  29. package/dist/catalog/org-plugin-uploads.d.ts +0 -1
  30. package/dist/catalog/org-plugin-uploads.js +0 -3
  31. package/dist/catalog/org-plugin-uploads.schemas.d.ts +0 -1
  32. package/dist/catalog/org-plugin-uploads.schemas.js +0 -2
  33. package/dist/catalog/platform-components.d.ts +0 -1
  34. package/dist/catalog/platform-components.js +3 -8
  35. package/dist/catalog/plugin-import.d.ts +0 -8
  36. package/dist/catalog/plugin-import.js +4 -35
  37. package/dist/catalog/plugin-install.d.ts +2 -2
  38. package/dist/catalog/plugin-install.js +2 -2
  39. package/dist/catalog/plugin-package.d.ts +7 -0
  40. package/dist/catalog/plugin-package.js +27 -1
  41. package/dist/catalog/plugin-upgrade.d.ts +5 -6
  42. package/dist/catalog/plugin-upgrade.js +5 -23
  43. package/dist/catalog/requests.service.d.ts +66 -10
  44. package/dist/catalog/requests.service.js +196 -15
  45. package/dist/channels/actions.d.ts +2 -0
  46. package/dist/channels/actions.js +19 -4
  47. package/dist/channels/cards.d.ts +16 -2
  48. package/dist/channels/cards.js +40 -7
  49. package/dist/channels/channels.module.js +3 -0
  50. package/dist/channels/outbound.d.ts +22 -1
  51. package/dist/channels/outbound.js +116 -7
  52. package/dist/channels/phrases.d.ts +25 -0
  53. package/dist/channels/phrases.js +19 -0
  54. package/dist/channels/threads.d.ts +12 -0
  55. package/dist/channels/threads.js +34 -0
  56. package/dist/config/schema.d.ts +23 -1
  57. package/dist/config/schema.js +105 -5
  58. package/dist/conversations/conversations.service.d.ts +42 -9
  59. package/dist/conversations/conversations.service.js +57 -25
  60. package/dist/conversations/events.d.ts +106 -11
  61. package/dist/conversations/events.js +121 -18
  62. package/dist/conversations/message-context.d.ts +6 -3
  63. package/dist/conversations/message-context.js +14 -10
  64. package/dist/conversations/queue.d.ts +15 -2
  65. package/dist/conversations/queue.js +22 -3
  66. package/dist/conversations/queue.service.d.ts +12 -6
  67. package/dist/conversations/queue.service.js +36 -13
  68. package/dist/db/kernel-schema.d.ts +105 -3
  69. package/dist/devices/conversation-devices.d.ts +8 -1
  70. package/dist/devices/conversation-devices.js +8 -0
  71. package/dist/devices/devices.controller.d.ts +21 -4
  72. package/dist/devices/devices.controller.js +46 -8
  73. package/dist/devices/devices.schemas.d.ts +9 -1
  74. package/dist/devices/devices.schemas.js +11 -3
  75. package/dist/devices/devices.service.d.ts +176 -4
  76. package/dist/devices/devices.service.js +341 -6
  77. package/dist/devices/dto.d.ts +56 -3
  78. package/dist/devices/dto.js +51 -1
  79. package/dist/devices/public-suffix.d.ts +6 -0
  80. package/dist/devices/public-suffix.js +79 -0
  81. package/dist/errors.d.ts +46 -5
  82. package/dist/errors.js +46 -5
  83. package/dist/evals/reconstruct.d.ts +18 -13
  84. package/dist/evals/reconstruct.js +50 -36
  85. package/dist/evals/recorded-tools.d.ts +7 -7
  86. package/dist/evals/recorded-tools.js +4 -3
  87. package/dist/evals/trajectory.d.ts +6 -1
  88. package/dist/evals/trajectory.js +7 -0
  89. package/dist/extensions/package.js +2 -2
  90. package/dist/http/api.module.js +5 -0
  91. package/dist/inbox/inbox.module.js +2 -1
  92. package/dist/inbox/inbox.service.d.ts +19 -4
  93. package/dist/inbox/inbox.service.js +14 -3
  94. package/dist/infra/config/kernel-config.service.js +11 -0
  95. package/dist/infra/events/kernel-events.d.ts +2 -1
  96. package/dist/local-devices/device-policy.service.d.ts +65 -0
  97. package/dist/local-devices/device-policy.service.js +217 -0
  98. package/dist/local-devices/local-device-gateway.js +4 -1
  99. package/dist/local-devices/local-device-provider.d.ts +40 -3
  100. package/dist/local-devices/local-device-provider.js +109 -7
  101. package/dist/local-devices/local-devices.module.d.ts +2 -1
  102. package/dist/local-devices/local-devices.module.js +19 -3
  103. package/dist/local-devices/machines.d.ts +14 -3
  104. package/dist/local-devices/machines.js +41 -3
  105. package/dist/local-devices/network-grants.d.ts +104 -0
  106. package/dist/local-devices/network-grants.js +288 -0
  107. package/dist/local-devices/policy-summary.d.ts +20 -0
  108. package/dist/local-devices/policy-summary.js +63 -0
  109. package/dist/orgs/orgs.module.js +4 -10
  110. package/dist/orgs/roles.d.ts +1 -1
  111. package/dist/orgs/roles.js +4 -0
  112. package/dist/orgs/settings.controller.js +2 -1
  113. package/dist/orgs/settings.service.d.ts +164 -12
  114. package/dist/orgs/settings.service.js +212 -24
  115. package/dist/platform/exclusions.js +11 -1
  116. package/dist/platform/operation.js +4 -0
  117. package/dist/platform/ops/admin-items.ops.js +1 -1
  118. package/dist/platform/ops/agent-skills.ops.js +2 -2
  119. package/dist/platform/ops/agent-templates.ops.js +2 -2
  120. package/dist/platform/ops/agents.ops.js +11 -15
  121. package/dist/platform/ops/devices.ops.js +3 -1
  122. package/dist/platform/ops/inbox.ops.js +19 -0
  123. package/dist/platform/ops/org-settings.ops.js +14 -5
  124. package/dist/platform/ops/platform.ops.d.ts +9 -1
  125. package/dist/platform/ops/platform.ops.js +34 -12
  126. package/dist/platform/ops/requests.ops.js +3 -2
  127. package/dist/platform/ops/schedules.ops.d.ts +21 -0
  128. package/dist/platform/ops/schedules.ops.js +470 -0
  129. package/dist/platform/ops/skills.ops.js +3 -3
  130. package/dist/platform/platform-ops.module.d.ts +2 -1
  131. package/dist/platform/platform-ops.module.js +5 -0
  132. package/dist/platform/route-schemas.js +3 -0
  133. package/dist/roles/worker.module.js +3 -0
  134. package/dist/runtime/context/build.js +13 -4
  135. package/dist/runtime/context/compaction.js +6 -3
  136. package/dist/runtime/context/device-paths.d.ts +43 -21
  137. package/dist/runtime/context/device-paths.js +104 -21
  138. package/dist/runtime/context/run-context.d.ts +9 -0
  139. package/dist/runtime/context/run-context.js +8 -0
  140. package/dist/runtime/context/skills.d.ts +14 -1
  141. package/dist/runtime/context/skills.js +36 -5
  142. package/dist/runtime/maintenance.d.ts +4 -2
  143. package/dist/runtime/maintenance.js +16 -5
  144. package/dist/runtime/run-finish.d.ts +6 -1
  145. package/dist/runtime/run-finish.js +16 -2
  146. package/dist/runtime/run-queue.js +21 -2
  147. package/dist/runtime/run-worker.js +14 -33
  148. package/dist/runtime/runtime.module.js +3 -0
  149. package/dist/runtime/subagents/definition.d.ts +32 -14
  150. package/dist/runtime/subagents/definition.js +106 -109
  151. package/dist/runtime/subagents/dispatch.js +4 -3
  152. package/dist/runtime/subagents/preamble.d.ts +2 -1
  153. package/dist/runtime/subagents/preamble.js +2 -1
  154. package/dist/runtime/waits.js +14 -4
  155. package/dist/sandbox/sandbox.service.d.ts +120 -10
  156. package/dist/sandbox/sandbox.service.js +335 -28
  157. package/dist/sandbox/tiers.d.ts +86 -0
  158. package/dist/sandbox/tiers.js +96 -0
  159. package/dist/schedules/effective-settings.d.ts +16 -0
  160. package/dist/schedules/effective-settings.js +17 -0
  161. package/dist/schedules/errors.d.ts +8 -0
  162. package/dist/schedules/errors.js +8 -0
  163. package/dist/schedules/fire.d.ts +118 -0
  164. package/dist/schedules/fire.js +625 -0
  165. package/dist/schedules/grant-rules.d.ts +94 -0
  166. package/dist/schedules/grant-rules.js +126 -0
  167. package/dist/schedules/job-ids.d.ts +32 -0
  168. package/dist/schedules/job-ids.js +27 -0
  169. package/dist/schedules/pause.d.ts +9 -0
  170. package/dist/schedules/pause.js +33 -0
  171. package/dist/schedules/run-tracker.d.ts +81 -0
  172. package/dist/schedules/run-tracker.js +333 -0
  173. package/dist/schedules/scan.job.d.ts +64 -0
  174. package/dist/schedules/scan.job.js +303 -0
  175. package/dist/schedules/schedule-math.d.ts +31 -0
  176. package/dist/schedules/schedule-math.js +223 -0
  177. package/dist/schedules/scheduled-tasks.service.d.ts +351 -0
  178. package/dist/schedules/scheduled-tasks.service.js +1193 -0
  179. package/dist/schedules/schedules.controller.d.ts +68 -0
  180. package/dist/schedules/schedules.controller.js +242 -0
  181. package/dist/schedules/schedules.module.d.ts +16 -0
  182. package/dist/schedules/schedules.module.js +65 -0
  183. package/dist/schedules/schedules.schemas.d.ts +316 -0
  184. package/dist/schedules/schedules.schemas.js +186 -0
  185. package/dist/schedules/store.d.ts +81 -0
  186. package/dist/schedules/store.js +390 -0
  187. package/dist/schedules/store.module.d.ts +6 -0
  188. package/dist/schedules/store.module.js +21 -0
  189. package/dist/schedules/types.d.ts +108 -0
  190. package/dist/schedules/types.js +1 -0
  191. package/dist/schedules/unattended.d.ts +117 -0
  192. package/dist/schedules/unattended.js +228 -0
  193. package/dist/setup/oauth-token-probe.js +8 -4
  194. package/dist/skills/agent-skills.service.d.ts +4 -8
  195. package/dist/skills/agent-skills.service.js +18 -53
  196. package/dist/skills/catalog.d.ts +65 -62
  197. package/dist/skills/catalog.js +109 -206
  198. package/dist/skills/conversation-catalog.d.ts +3 -3
  199. package/dist/skills/conversation-catalog.js +5 -12
  200. package/dist/skills/invoke.d.ts +2 -2
  201. package/dist/skills/skills.controller.d.ts +3 -1
  202. package/dist/skills/skills.service.d.ts +38 -37
  203. package/dist/skills/skills.service.js +168 -107
  204. package/dist/skills/usable.d.ts +57 -8
  205. package/dist/skills/usable.js +172 -38
  206. package/dist/tools/approvals.controller.d.ts +4 -2
  207. package/dist/tools/approvals.controller.js +17 -2
  208. package/dist/tools/approvals.schemas.d.ts +1 -0
  209. package/dist/tools/approvals.schemas.js +3 -1
  210. package/dist/tools/approvals.service.d.ts +182 -7
  211. package/dist/tools/approvals.service.js +597 -13
  212. package/dist/tools/builtin-names.d.ts +25 -4
  213. package/dist/tools/builtin-names.js +45 -4
  214. package/dist/tools/builtins/builtin-tools.d.ts +8 -0
  215. package/dist/tools/builtins/builtin-tools.js +23 -2
  216. package/dist/tools/builtins/sandbox/bash.js +16 -7
  217. package/dist/tools/builtins/sandbox/common.d.ts +1 -1
  218. package/dist/tools/builtins/sandbox/common.js +33 -10
  219. package/dist/tools/builtins/sandbox/edit.js +7 -3
  220. package/dist/tools/builtins/sandbox/file-state.d.ts +31 -1
  221. package/dist/tools/builtins/sandbox/file-state.js +33 -3
  222. package/dist/tools/builtins/sandbox/write.js +8 -4
  223. package/dist/tools/builtins/schedules.d.ts +107 -0
  224. package/dist/tools/builtins/schedules.js +789 -0
  225. package/dist/tools/elicitation.d.ts +2 -0
  226. package/dist/tools/elicitation.js +1 -1
  227. package/dist/tools/fixed-ask.d.ts +7 -0
  228. package/dist/tools/fixed-ask.js +9 -0
  229. package/dist/tools/network-asks.d.ts +109 -0
  230. package/dist/tools/network-asks.js +221 -0
  231. package/dist/tools/pipeline.d.ts +53 -6
  232. package/dist/tools/pipeline.js +202 -13
  233. package/dist/tools/policy.d.ts +21 -8
  234. package/dist/tools/policy.js +63 -9
  235. package/dist/tools/registry.d.ts +10 -1
  236. package/dist/tools/tools.module.js +12 -0
  237. package/package.json +2 -2
  238. package/src/migrations/0113_scheduled_tasks.sql +108 -0
  239. package/src/migrations/0114_network_asks.sql +23 -0
  240. package/src/migrations/0115_network_requests.sql +28 -0
  241. package/src/migrations/0116_skills_default_on.sql +37 -0
  242. package/src/migrations/0117_sandbox_tiers.sql +11 -0
@@ -22,6 +22,8 @@ import { KernelDb } from "../infra/database/kernel-db.js";
22
22
  import { TransactionHost } from "../infra/database/transaction-host.js";
23
23
  import { Transactional } from "../infra/database/transactional.decorator.js";
24
24
  import { KERNEL_LOGGER } from "../infra/tokens.js";
25
+ import { DevicePolicyService } from "../local-devices/device-policy.service.js";
26
+ import { NetworkGrants } from "../local-devices/network-grants.js";
25
27
  import { UNKNOWN_RESULT_TEXT } from "../runtime/context/pairing.js";
26
28
  import { fireObserve, hookDeniedAudit, hookDeniedEvent, hookScope, } from "../runtime/hook-calls.js";
27
29
  import { subagentWaitingIdemKey } from "../runtime/loop.js";
@@ -30,8 +32,9 @@ import { rootSurfaceOf } from "../runtime/subagents/child.js";
30
32
  import { SubagentMessages } from "../runtime/subagents/messages.js";
31
33
  import { pairStoppedForks } from "../runtime/subagents/skill-fork.js";
32
34
  import { SubagentsService } from "../runtime/subagents/subagents.service.js";
35
+ import { appendMissingGrant, connectionGrantKey, UNATTENDED_CONNECTION_ERROR, UNATTENDED_ELICITATION_NOTE, UNATTENDED_QUESTION_ERROR, unattendedFails, unattendedRunOf, } from "../schedules/unattended.js";
33
36
  import { skillLoadOfferedIn } from "../skills/catalog.js";
34
- import { ApprovalsService } from "./approvals.service.js";
37
+ import { ApprovalsService, } from "./approvals.service.js";
35
38
  import { Breaker, breakerOpenMessage } from "./breaker.js";
36
39
  import { SEND_MESSAGE, SUBAGENT_HANDBACK, TOOL_SEARCH } from "./builtin-names.js";
37
40
  import { AGENT_TOOL, AgentToolInput } from "./builtins/agent.js";
@@ -43,6 +46,7 @@ import { CredentialMissing, Credentials } from "./credentials.js";
43
46
  import { ToolDrift } from "./drift.js";
44
47
  import { ElicitationSession } from "./elicitation.js";
45
48
  import { legacyHint, legacyToolInput, legacyToolKey } from "./legacy-keys.js";
49
+ import { blockedEventHosts, clipKept, NETWORK_ASK_KEPT_MAX_BYTES, networkBlockedIdemKey, planNetworkAsk, settledNetworkNotes, withNetworkNotes, } from "./network-asks.js";
46
50
  import { OrgToolSwitches, toolTurnedOffReason } from "./org-tool-switches.js";
47
51
  import { renderResult, resolveOutputRefs, spillIfLarge, ToolOutputs } from "./outputs.js";
48
52
  import { createDecidePolicy } from "./policy.js";
@@ -182,6 +186,26 @@ function withNotes(result, notes) {
182
186
  content: [...content, ...notes.map((text) => ({ type: "text", text }))],
183
187
  };
184
188
  }
189
+ /** A network ask's kept display fields, written with its result when the ask settles. */
190
+ const keptExtras = (kept) => ({
191
+ ...(kept?.outputId !== undefined && { outputId: kept.outputId }),
192
+ ...(kept?.contentTypes !== undefined && { contentTypes: kept.contentTypes }),
193
+ ...(kept?.display !== undefined && { display: kept.display }),
194
+ ...(kept?.view !== undefined && { view: kept.view }),
195
+ });
196
+ /** A builtin result's kernel-internal network report (see `BuiltinResult.networkReport`). */
197
+ function networkReportOf(result) {
198
+ const r = result?.networkReport;
199
+ if (r === undefined || typeof r.deviceId !== "string" || !Array.isArray(r.blocked)) {
200
+ return undefined;
201
+ }
202
+ const blocked = r.blocked.filter((b) => typeof b === "object" &&
203
+ b !== null &&
204
+ typeof b.host === "string" &&
205
+ typeof b.port === "number" &&
206
+ ["device", "org", "address", "unknown"].includes(b.layer));
207
+ return blocked.length === 0 ? undefined : { deviceId: r.deviceId, blocked };
208
+ }
185
209
  /** 审批的等待时长(小时,四舍五入,至少 1)。 */
186
210
  const approvalHours = (a) => Math.max(1, Math.round((new Date(a.expiresAt).getTime() - new Date(a.createdAt).getTime()) / 3_600_000));
187
211
  const approvalWait = (a) => ({
@@ -212,7 +236,7 @@ let ToolPipeline = class ToolPipeline {
212
236
  /** The message tools between agents; absent (tests): their calls are refused. */
213
237
  messages, registry, credentials, breaker, outputs, approvals, questions, config, log,
214
238
  /** Plugin hooks and inline ui contributions; absent (tests): no hooks, cards dropped. */
215
- host, drift, effective, switches, txHost, hooks) {
239
+ host, drift, effective, switches, txHost, hooks, networkGrants, devicePolicy) {
216
240
  this.config = config;
217
241
  this.txHost = txHost;
218
242
  this.deps = {
@@ -244,6 +268,9 @@ let ToolPipeline = class ToolPipeline {
244
268
  return subagents ? { dispatch: (a) => subagents.dispatch(a) } : undefined;
245
269
  },
246
270
  messages,
271
+ network: networkGrants && devicePolicy
272
+ ? { grants: networkGrants, devices: devicePolicy }
273
+ : undefined,
247
274
  };
248
275
  const decide = hooks?.decidePolicy;
249
276
  // the default policy reads live, on the context's handle
@@ -256,12 +283,21 @@ let ToolPipeline = class ToolPipeline {
256
283
  * 某个调用需要等人处理(审批 / 用户输入)→ 返回 `suspended`,之后的调用留到续跑时处理;
257
284
  * 续跑时重新进入本函数,按 PG 里的审批 / 输入请求结果继续。
258
285
  */
259
- async runStep(ctx) {
286
+ async runStep(given) {
287
+ let ctx = given;
260
288
  const { eventLog } = this.deps;
261
289
  const msg = await eventLog.findByIdem(ctx.conversationId, assistantIdemKey(ctx.runId, ctx.step));
262
290
  if (msg?.type !== "assistant_message")
263
291
  return { kind: "done" };
264
292
  const calls = msg.payload.content.flatMap((b) => b.type === "tool_call" ? [{ id: b.id, name: b.name, input: b.input }] : []);
293
+ // scheduled tasks spec §7: read live for each step (a person's message absorbed since makes the
294
+ // rest of the run attended)
295
+ if (calls.length > 0 && ctx.unattended === undefined) {
296
+ ctx = {
297
+ ...ctx,
298
+ unattended: await unattendedRunOf(this.deps.db, this.config, ctx.orgId, ctx.runId),
299
+ };
300
+ }
265
301
  let resolved;
266
302
  const resolve = async () => {
267
303
  // 每步实时解析(连接器 / 插件状态可能在模型调用之后变化)
@@ -324,6 +360,22 @@ let ToolPipeline = class ToolPipeline {
324
360
  const attempt = rounds.length + 1;
325
361
  const started = await this.deps.eventLog.findByIdem(ctx.conversationId, idem.started(ctx.runId, call.id, attempt));
326
362
  if (started?.type === "tool_call_started") {
363
+ // a command that ran and was refused a network host waits for the device owner (network
364
+ // ask-to-allow spec §6.2, P-D2): pending → wait on; settled → its kept result and the
365
+ // outcome's note (the command is never run again; its tool.called was audited when it ran)
366
+ const ask = await this.deps.approvals.networkForCall(ctx.runId, call.id);
367
+ if (ask !== null) {
368
+ const notes = settledNetworkNotes(ask, approvalHours(ask));
369
+ if (notes === null)
370
+ return approvalWait(ask);
371
+ const kept = ask.detail?.result;
372
+ await this.writeResult(ctx, call.id, call.name, {
373
+ ...keptExtras(kept),
374
+ content: withNetworkNotes(kept?.content ?? UNKNOWN_RESULT_TEXT, notes),
375
+ isError: kept?.isError ?? true,
376
+ });
377
+ return undefined;
378
+ }
327
379
  // 确实开始执行过:照样审计 tool.called(耗时未知);放行依据只认批准了所执行定义的审批记录
328
380
  const key = started.payload.toolKey;
329
381
  const approval = await this.deps.approvals.forCall(ctx.runId, call.id);
@@ -439,9 +491,16 @@ let ToolPipeline = class ToolPipeline {
439
491
  ...(ctx.parent.otherAgent !== undefined && { parentAgentDiffers: ctx.parent.otherAgent }),
440
492
  }),
441
493
  callPolicy,
494
+ ...(ctx.unattended != null && { unattended: ctx.unattended }),
442
495
  });
443
496
  if (decision === "deny")
444
497
  return fail(deniedText(tool.key, callPolicy?.key), outKey);
498
+ if (typeof decision === "object" && "refused" in decision) {
499
+ if (decision.missingGrant !== undefined && ctx.unattended != null) {
500
+ await appendMissingGrant(this.deps.db, ctx.unattended.taskRunId, decision.missingGrant);
501
+ }
502
+ return fail(decision.refused, outKey);
503
+ }
445
504
  if (typeof decision === "object" && "unavailable" in decision) {
446
505
  return fail(unavailableText(tool.key, decision.unavailable), outKey);
447
506
  }
@@ -620,6 +679,16 @@ let ToolPipeline = class ToolPipeline {
620
679
  let outcome = await this.execute(tool, input, ctx, call.id, resume, resolved.tools);
621
680
  const durationMs = Date.now() - t0;
622
681
  await this.deps.hooks?.afterExecute?.(ctx.runId, call.id);
682
+ // scheduled tasks spec §7, `fail` mode: no one answers an input request or connects an app
683
+ if (unattendedFails(ctx.unattended) && ctx.unattended != null) {
684
+ if (outcome.kind === "input_required") {
685
+ outcome = { kind: "error", message: UNATTENDED_QUESTION_ERROR };
686
+ }
687
+ else if (outcome.kind === "connection_required") {
688
+ await appendMissingGrant(this.deps.db, ctx.unattended.taskRunId, connectionGrantKey(outcome.connectorSlug));
689
+ outcome = { kind: "error", message: UNATTENDED_CONNECTION_ERROR(outcome.connectorName) };
690
+ }
691
+ }
623
692
  if (outcome.kind === "input_required") {
624
693
  const asked = await this.askInput(ctx, call.id, tool.key, attempt, outcome);
625
694
  if (typeof asked !== "string")
@@ -673,8 +742,40 @@ let ToolPipeline = class ToolPipeline {
673
742
  },
674
743
  ]
675
744
  : [];
745
+ const callAudit = {
746
+ durationMs,
747
+ connectorId: tool.source.kind === "connector" ? tool.source.connectorId : null,
748
+ authorization,
749
+ };
750
+ // a foreground command refused network hosts (network ask-to-allow spec §6.2): display-only
751
+ // `network_blocked` before the result, then an ask to the device owner (the run waits; the
752
+ // result is written when the ask settles) or notes after the output
753
+ const report = outcome.kind === "result" && tool.source.kind === "builtin"
754
+ ? networkReportOf(outcome.result)
755
+ : undefined;
756
+ let content = spilled.content;
757
+ if (report !== undefined) {
758
+ const handled = await this.networkBlocked(ctx, call.id, outKey, input, report, callAudit, {
759
+ text: rendered.text,
760
+ content: spilled.content,
761
+ isError: rendered.isError,
762
+ ...(spilled.outputId !== undefined && { outputId: spilled.outputId }),
763
+ ...(rendered.contentTypes.length > 0 && { contentTypes: rendered.contentTypes }),
764
+ ...(display !== undefined && { display }),
765
+ ...(view !== undefined && { view }),
766
+ });
767
+ if (handled.kind === "wait") {
768
+ if (hooks && hookBase) {
769
+ fireObserve(hooks, "tool.postExecute", { ...hookBase, isError: rendered.isError, content: handled.content, durationMs }, ctx.signal, this.deps.log);
770
+ }
771
+ return handled.wait;
772
+ }
773
+ if (handled.event !== null)
774
+ pre.push(handled.event);
775
+ content = withNetworkNotes(content, handled.notes);
776
+ }
676
777
  await this.writeResult(ctx, call.id, outKey, {
677
- content: spilled.content,
778
+ content,
678
779
  isError: rendered.isError,
679
780
  ...(spilled.outputId !== undefined && { outputId: spilled.outputId }),
680
781
  ...(rendered.contentTypes.length > 0 && { contentTypes: rendered.contentTypes }),
@@ -682,11 +783,7 @@ let ToolPipeline = class ToolPipeline {
682
783
  ...(display !== undefined && { display }),
683
784
  ...(view !== undefined && { view }),
684
785
  ...(card !== undefined && { card }),
685
- }, pre, {
686
- durationMs,
687
- connectorId: tool.source.kind === "connector" ? tool.source.connectorId : null,
688
- authorization,
689
- });
786
+ }, pre, callAudit);
690
787
  // a sub-agent that needs a personal connection: mirrored in the root conversation of its tree
691
788
  // for display (sub-agents spec §9, agent map spec §7; not a wait, the child got the connect
692
789
  // text as its result)
@@ -697,10 +794,94 @@ let ToolPipeline = class ToolPipeline {
697
794
  }));
698
795
  }
699
796
  if (hooks && hookBase) {
700
- fireObserve(hooks, "tool.postExecute", { ...hookBase, isError: rendered.isError, content: spilled.content, durationMs }, ctx.signal, this.deps.log);
797
+ fireObserve(hooks, "tool.postExecute", { ...hookBase, isError: rendered.isError, content, durationMs }, ctx.signal, this.deps.log);
701
798
  }
702
799
  return undefined;
703
800
  }
801
+ /**
802
+ * A foreground command's refused network hosts (network ask-to-allow spec §6.2, §6.8, §8.3):
803
+ * the `network_blocked` event, the missing grants of an unattended run, and either notes for the
804
+ * result written now, or (device-layer hosts worth asking) `network_blocked` and the call's
805
+ * `tool.called` audit written now, the result kept in a network ask to the device's owner and
806
+ * the run's wait. The kept result is the formatted one (≤ 16 KB: larger output is archived)
807
+ * followed by the notes written now; it is written with the decision's note when the ask
808
+ * settles (P-D2).
809
+ */
810
+ async networkBlocked(ctx, callId, toolKey, input, report, callAudit, result) {
811
+ const hosts = blockedEventHosts(report.blocked);
812
+ if (hosts.length === 0)
813
+ return { kind: "write", event: null, notes: [] };
814
+ const event = {
815
+ type: "network_blocked",
816
+ runId: ctx.runId,
817
+ idemKey: networkBlockedIdemKey(ctx.runId, callId),
818
+ payload: { step: ctx.step, callId, hosts },
819
+ };
820
+ const network = this.deps.network;
821
+ if (network === undefined)
822
+ return { kind: "write", event, notes: [] };
823
+ const device = await network.devices.summary(report.deviceId);
824
+ const run = hosts.some((h) => h.layer === "device")
825
+ ? await network.grants.runState(ctx.runId, report.deviceId)
826
+ : null;
827
+ const plan = planNetworkAsk(hosts, run, { device, unattended: ctx.unattended });
828
+ if (ctx.unattended != null) {
829
+ for (const key of plan.missingGrants) {
830
+ await appendMissingGrant(this.deps.db, ctx.unattended.taskRunId, key);
831
+ }
832
+ }
833
+ if (plan.ask.length === 0)
834
+ return { kind: "write", event, notes: plan.notes };
835
+ // the device's owner is the only person who may decide (spec §5.2)
836
+ const owner = await this.deps.db
837
+ .selectFrom("devices")
838
+ .select("owner_account_id")
839
+ .where("id", "=", report.deviceId)
840
+ .where("org_id", "=", ctx.orgId)
841
+ .where("deleted_at", "is", null)
842
+ .executeTakeFirst();
843
+ if (owner === undefined)
844
+ return { kind: "write", event, notes: plan.notes };
845
+ let { content, outputId } = result;
846
+ if (Buffer.byteLength(content, "utf8") > NETWORK_ASK_KEPT_MAX_BYTES && outputId === undefined) {
847
+ // output under the spill threshold but over what an ask keeps: archive it after all
848
+ const spilled = await spillIfLarge(result.text, { spillChars: 0, previewChars: this.config.get().tools.outputPreviewChars }, (c) => this.deps.outputs.save({
849
+ orgId: ctx.orgId,
850
+ conversationId: ctx.conversationId,
851
+ callId,
852
+ content: c,
853
+ }));
854
+ content = spilled.content;
855
+ outputId = spilled.outputId;
856
+ }
857
+ const { text: _text, ...rest } = result;
858
+ const kept = {
859
+ ...rest,
860
+ content: withNetworkNotes(clipKept(content), plan.notes),
861
+ ...(outputId !== undefined && { outputId }),
862
+ };
863
+ // the command ran: network_blocked and its tool.called now, the result once the ask settles
864
+ const written = await this.writeResultIn(ctx, callId, toolKey, { isError: result.isError }, [event], callAudit, []);
865
+ await this.deps.eventLog.notify(ctx.conversationId, Math.max(...written.map((e) => e.seq)));
866
+ const given = (input ?? {});
867
+ const ask = await this.deps.approvals.requestNetwork({
868
+ orgId: ctx.orgId,
869
+ conversationId: ctx.conversationId,
870
+ runId: ctx.runId,
871
+ step: ctx.step,
872
+ callId,
873
+ requestedFor: owner.owner_account_id,
874
+ detail: {
875
+ deviceId: report.deviceId,
876
+ hosts: plan.ask,
877
+ command: typeof given.command === "string" ? clipText(given.command, 2000) : "",
878
+ description: typeof given.description === "string" ? given.description : null,
879
+ result: kept,
880
+ },
881
+ scopes: plan.scopes,
882
+ });
883
+ return { kind: "wait", wait: approvalWait(ask), content: kept.content };
884
+ }
704
885
  /** Appends a sub-agent's connect card to its root conversation ({@link rootSurfaceOf}). */
705
886
  async surfaceConnectInRoot(ctx, parent, callId, toolKey) {
706
887
  const target = await rootSurfaceOf(this.deps.db, ctx.conversationId, ctx.runId, parent);
@@ -886,7 +1067,7 @@ let ToolPipeline = class ToolPipeline {
886
1067
  const connectorId = source.connectorId;
887
1068
  const app = await db
888
1069
  .selectFrom("connectors")
889
- .select(["name", "connector", "endpoint", "auth", "status"])
1070
+ .select(["name", "slug", "connector", "endpoint", "auth", "status"])
890
1071
  .where("id", "=", connectorId)
891
1072
  .where("org_id", "=", orgId)
892
1073
  .where("deleted_at", "is", null)
@@ -922,10 +1103,13 @@ let ToolPipeline = class ToolPipeline {
922
1103
  // 远端在调用途中向用户要输入(MCP elicitation,见 elicitation.ts):需要问用户时中止本次调用、按 input_required
923
1104
  // 处理;续跑时用 ctx.resume 的答案答复远端再次发起的同一请求。resources 工具不续跑,一律拒绝。
924
1105
  const suspend = new AbortController();
1106
+ // an unattended run in `fail` mode declines every elicitation: no one is there to answer
1107
+ const unattended = unattendedFails(ctx.unattended);
925
1108
  const elicit = new ElicitationSession({
926
1109
  resume,
927
1110
  onSuspend: () => suspend.abort(),
928
- declineAll: source.resources === true,
1111
+ declineAll: source.resources === true || unattended,
1112
+ ...(unattended && { declineNote: UNATTENDED_ELICITATION_NOTE }),
929
1113
  });
930
1114
  // 远端可能把收到的凭据回显在结果 / 错误 / elicitation 表单里:写入事件之前按本次注入的值打码(见 redact.ts)
931
1115
  const clean = (o) => {
@@ -961,6 +1145,7 @@ let ToolPipeline = class ToolPipeline {
961
1145
  kind: "connection_required",
962
1146
  connectorId,
963
1147
  connectorName: app.name,
1148
+ connectorSlug: app.slug,
964
1149
  message: personalMissingText(app.name),
965
1150
  }
966
1151
  : { kind: "error", message: orgMissingText(app.name) };
@@ -1251,6 +1436,10 @@ ToolPipeline = __decorate([
1251
1436
  __param(15, Inject(OrgToolSwitches)),
1252
1437
  __param(16, Inject(TransactionHost)),
1253
1438
  __param(17, Optional()),
1254
- __param(17, Inject(TOOL_PIPELINE_HOOKS))
1439
+ __param(17, Inject(TOOL_PIPELINE_HOOKS)),
1440
+ __param(18, Optional()),
1441
+ __param(18, Inject(NetworkGrants)),
1442
+ __param(19, Optional()),
1443
+ __param(19, Inject(DevicePolicyService))
1255
1444
  ], ToolPipeline);
1256
1445
  export { ToolPipeline };
@@ -1,6 +1,13 @@
1
1
  import type { KernelSchemaDb } from "../db/kernel-schema.js";
2
2
  import type { DecidePolicy } from "./pipeline.js";
3
- import type { ResolvedTool, ToolPolicy } from "./registry.js";
3
+ import type { ToolPolicy } from "./registry.js";
4
+ export { R3_FIXED_ASK_TOOL_KEYS } from "./builtin-names.js";
5
+ export { isFixedAsk } from "./fixed-ask.js";
6
+ /**
7
+ * What allowed an ask without an approval: this conversation's grant, an "always" grant, or a
8
+ * scheduled task's grant (its unattended runs, scheduled tasks spec §7).
9
+ */
10
+ export type GrantKind = "session" | "always" | "task";
4
11
  /** 取更严的策略(`allow < ask < deny`)。 */
5
12
  export declare function stricter(a: ToolPolicy, b: ToolPolicy | undefined): ToolPolicy;
6
13
  export interface PolicyInput {
@@ -10,19 +17,16 @@ export interface PolicyInput {
10
17
  agentOverride?: "ask" | "deny";
11
18
  /** `requiresUserInteraction`。 */
12
19
  fixedAsk: boolean;
13
- /** 已记住且 specHash 与当前一致的授权。 */
14
- grant: "session" | "always" | null;
20
+ /**
21
+ * 已记住且 specHash 与当前一致的授权; `task`: a scheduled task's grant in its unattended run.
22
+ */
23
+ grant: GrantKind | null;
15
24
  }
16
25
  /**
17
26
  * 策略判定(纯函数):取组织默认、agent 收紧、固定询问三者中最严者;
18
27
  * 记住的授权只能把 `ask` 放行为 `allow`,不能越过 `deny`,对 `requiresUserInteraction` 的工具无效。
19
28
  */
20
29
  export declare function decide(input: PolicyInput): ToolPolicy;
21
- /**
22
- * Whether the tool is a fixed ask: its spec declares `requiresUserInteraction`, so its policy is
23
- * at least `ask` and remembered grants never let it through.
24
- */
25
- export declare function isFixedAsk(tool: ResolvedTool): boolean;
26
30
  /** Which level decided a connector tool's policy ({@link connectorPolicy}). */
27
31
  export type ConnectorPolicySource = "org" | "member" | "agent";
28
32
  /**
@@ -63,5 +67,14 @@ export declare function connectorPolicy(input: {
63
67
  * (`approverRole` = 所需角色、`requestedFor` = null),此时记住的授权不放行。
64
68
  *
65
69
  * 结果为 `ask` 时返回审批请求,默认指向发起人本人;记住的授权把 `ask` 放行时返回 `{ grant }`(按 allow 执行)。
70
+ *
71
+ * An unattended run (`ctx.unattended`, not a trial run; scheduled tasks spec §7):
72
+ * - a tool in the org's `forbiddenTools` (by tool key or per-call key) is denied;
73
+ * - a Platform call that changes the agent's skills (`agents.register-skill`, ...) is an ask no
74
+ * grant covers, even on a private agent where it would run without asking;
75
+ * - an ask is also allowed by a task grant of the current definition (`{ grant: "task" }`), never
76
+ * for a fixed ask, a role escalation or a connector tool whose definition changed;
77
+ * - in `fail` mode an ask still left is refused (`{ refused, missingGrant }`, no approval), and
78
+ * `AskUserQuestion` is refused; in `wait` mode both ask the creator as in an attended run.
66
79
  */
67
80
  export declare function createDecidePolicy(db: KernelSchemaDb): DecidePolicy;
@@ -1,9 +1,15 @@
1
1
  import { sql } from "kysely";
2
2
  import { roleAtLeast } from "../orgs/roles.js";
3
3
  import { readOrgSettings } from "../orgs/settings.service.js";
4
- import { SANDBOX_ASK_TOOL_NAMES } from "./builtin-names.js";
4
+ import { grantRefusalOf } from "../schedules/grant-rules.js";
5
+ import { findTaskGrant } from "../schedules/store.js";
6
+ import { changesAgentSkills, UNATTENDED_ASK_ERROR, UNATTENDED_FIXED_ASK_ERROR, UNATTENDED_QUESTION_ERROR, unattendedRules, } from "../schedules/unattended.js";
7
+ import { BUILTIN, SANDBOX_ASK_TOOL_NAMES } from "./builtin-names.js";
5
8
  import { personalAuth } from "./connector-account-policies.js";
9
+ import { isFixedAsk } from "./fixed-ask.js";
6
10
  import { normalizeStoredDefinitionIn } from "./legacy-data.js";
11
+ export { R3_FIXED_ASK_TOOL_KEYS } from "./builtin-names.js";
12
+ export { isFixedAsk } from "./fixed-ask.js";
7
13
  const RANK = { allow: 0, ask: 1, deny: 2 };
8
14
  /** 取更严的策略(`allow < ask < deny`)。 */
9
15
  export function stricter(a, b) {
@@ -21,13 +27,6 @@ export function decide(input) {
21
27
  return "allow";
22
28
  return p;
23
29
  }
24
- /**
25
- * Whether the tool is a fixed ask: its spec declares `requiresUserInteraction`, so its policy is
26
- * at least `ask` and remembered grants never let it through.
27
- */
28
- export function isFixedAsk(tool) {
29
- return tool.spec.annotations?.requiresUserInteraction === true;
30
- }
31
30
  const asSetting = (v) => v === "allow" || v === "ask" || v === "deny" ? v : undefined;
32
31
  /** The stricter of the settings that are set (none set: undefined). */
33
32
  function combine(...xs) {
@@ -92,6 +91,15 @@ export function connectorPolicy(input) {
92
91
  * (`approverRole` = 所需角色、`requestedFor` = null),此时记住的授权不放行。
93
92
  *
94
93
  * 结果为 `ask` 时返回审批请求,默认指向发起人本人;记住的授权把 `ask` 放行时返回 `{ grant }`(按 allow 执行)。
94
+ *
95
+ * An unattended run (`ctx.unattended`, not a trial run; scheduled tasks spec §7):
96
+ * - a tool in the org's `forbiddenTools` (by tool key or per-call key) is denied;
97
+ * - a Platform call that changes the agent's skills (`agents.register-skill`, ...) is an ask no
98
+ * grant covers, even on a private agent where it would run without asking;
99
+ * - an ask is also allowed by a task grant of the current definition (`{ grant: "task" }`), never
100
+ * for a fixed ask, a role escalation or a connector tool whose definition changed;
101
+ * - in `fail` mode an ask still left is refused (`{ refused, missingGrant }`, no approval), and
102
+ * `AskUserQuestion` is refused; in `wait` mode both ask the creator as in an attended run.
95
103
  */
96
104
  export function createDecidePolicy(db) {
97
105
  return async (tool, agentDef, ctx) => {
@@ -150,6 +158,18 @@ export function createDecidePolicy(db) {
150
158
  memberSetting = mine?.policy;
151
159
  }
152
160
  }
161
+ // scheduled tasks spec §7: what an unattended run may not do at all
162
+ const unattended = unattendedRules(ctx.unattended) ? ctx.unattended : undefined;
163
+ if (unattended !== undefined) {
164
+ const forbidden = unattended.forbiddenTools;
165
+ if (forbidden.includes(tool.key) || forbidden.includes(policyKey))
166
+ return "deny";
167
+ if (unattended.unattendedAsk === "fail" &&
168
+ tool.source.kind === "builtin" &&
169
+ tool.key === BUILTIN.AskUserQuestion) {
170
+ return { refused: UNATTENDED_QUESTION_ERROR };
171
+ }
172
+ }
153
173
  // 沙箱工具的组织默认(M5 T8):组织设置 sandboxToolPolicy 为 ask 时,有副作用的沙箱工具至少 ask
154
174
  // `orgAsk`: the org setting, not the tool's own default, is what makes it ask
155
175
  let orgAsk = false;
@@ -202,6 +222,8 @@ export function createDecidePolicy(db) {
202
222
  agentOverride = undefined;
203
223
  }
204
224
  let escalateTo = null;
225
+ /** The acting account's role, read only for a tool that declares `requiredRole`. */
226
+ let memberRole = null;
205
227
  if (tool.requiredRole !== undefined) {
206
228
  const m = await db
207
229
  .selectFrom("memberships")
@@ -211,12 +233,20 @@ export function createDecidePolicy(db) {
211
233
  .executeTakeFirst();
212
234
  if (!m)
213
235
  return "deny";
236
+ memberRole = m.role;
214
237
  if (!roleAtLeast(m.role, tool.requiredRole))
215
238
  escalateTo = tool.requiredRole;
216
239
  }
217
240
  // `remember: false` on a call that is not a plain allow (an org / platform operation, or a read
218
241
  // an agent tightened to ask): every call asks, grants never apply. A read (`allow`) never asks.
219
- const fixedAsk = isFixedAsk(tool) ||
242
+ // an unattended run never changes the agent's skills on its own (spec §7): a Platform call
243
+ // that registers, deletes or shares one asks, outside every grant
244
+ const unattendedSkillChange = unattended !== undefined &&
245
+ tool.source.kind === "builtin" &&
246
+ tool.key === BUILTIN.Platform &&
247
+ changesAgentSkills(policyKey);
248
+ const fixedAsk = unattendedSkillChange ||
249
+ isFixedAsk(tool) ||
220
250
  (call !== null && !call.remember && (call.policy !== "allow" || agentOverride !== undefined));
221
251
  const input = {
222
252
  connectorDefault,
@@ -229,12 +259,36 @@ export function createDecidePolicy(db) {
229
259
  return "deny";
230
260
  if (pre === "ask" && !fixedAsk && escalateTo === null && hashCurrent) {
231
261
  input.grant = await findGrant(db, tool, policyKey, connectorId, ctx);
262
+ // an unattended run: the task's grants, like a tool_grants entry (spec §7)
263
+ if (input.grant === null &&
264
+ unattended !== undefined &&
265
+ (await findTaskGrant(db, unattended.taskId, policyKey, tool.specHash, connectorId))) {
266
+ input.grant = "task";
267
+ }
232
268
  }
233
269
  const result = decide(input);
234
270
  if (result === "allow" && escalateTo === null) {
235
271
  // 记住的授权把 ask 放行:返回授权种类(审计链记录凭什么放行)
236
272
  return pre === "ask" && input.grant !== null ? { grant: input.grant } : "allow";
237
273
  }
274
+ // an ask outside the grants in an unattended run, `fail` mode: no approval (spec §7)
275
+ if (unattended?.unattendedAsk === "fail") {
276
+ // recorded as a missing grant only when the creator could grant it to the task (the task
277
+ // grants' own rule) or could once promoted (`needs_role`, which the inbox says); a fixed
278
+ // ask, a per-call key (a skill change among them) or a forbidden tool never can: refused as a fixed ask,
279
+ // not recorded, so the inbox never offers a grant that cannot be added
280
+ const why = fixedAsk
281
+ ? { code: "fixed_ask" }
282
+ : grantRefusalOf(policyKey, tool, {
283
+ // only `requiredRole` reads the role, and then it was read above
284
+ role: memberRole ?? "guest",
285
+ forbiddenTools: unattended.forbiddenTools,
286
+ });
287
+ if (why !== null && why.code !== "needs_role") {
288
+ return { refused: UNATTENDED_FIXED_ASK_ERROR(policyKey) };
289
+ }
290
+ return { refused: UNATTENDED_ASK_ERROR(policyKey), missingGrant: policyKey };
291
+ }
238
292
  const ask = {
239
293
  toolKey: policyKey,
240
294
  specHash: tool.specHash,
@@ -1,4 +1,4 @@
1
- import type { Connector, HookParentScope, InputRequired, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
1
+ import type { Connector, HookParentScope, InputRequired, NetworkBlockedHost, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
2
2
  import { type AgentDefinition, type DisabledToolRef, type SubagentTools } from "../agents/definition.js";
3
3
  import type { ToolView } from "../conversations/events.js";
4
4
  import { EffectiveConfigs } from "../extensions/effective-config.js";
@@ -77,6 +77,15 @@ export type BuiltinResult = (ToolResult & {
77
77
  * checking it against `ToolViewSchema`). Builtins only: extension tools cannot set it.
78
78
  */
79
79
  view?: ToolView;
80
+ /**
81
+ * Kernel-internal (never written to the log or sent to the model): the network hosts a
82
+ * foreground command on a device was refused (`Bash`; network ask-to-allow spec §6.2). The
83
+ * pipeline turns it into `network_blocked`, an ask to the device owner or notes.
84
+ */
85
+ networkReport?: {
86
+ deviceId: string;
87
+ blocked: NetworkBlockedHost[];
88
+ };
80
89
  }) | InputRequired;
81
90
  /** 内置工具的处理函数(插件的 `ToolHandler` 可直接赋给它)。 */
82
91
  export type BuiltinHandler = (input: unknown, ctx: BuiltinCallContext) => Promise<BuiltinResult>;
@@ -12,6 +12,7 @@ import { EventLogModule } from "../conversations/event-log.js";
12
12
  import { DevicesModule } from "../devices/devices.module.js";
13
13
  import { ExtensionsModule } from "../extensions/extensions.module.js";
14
14
  import { QueueModule } from "../infra/queue/queue.module.js";
15
+ import { LocalDevicesModule } from "../local-devices/local-devices.module.js";
15
16
  import { ModelsModule } from "../models/models.module.js";
16
17
  import { OrgLockModule } from "../orgs/org-lock.js";
17
18
  import { PlatformModule } from "../platform/platform.module.js";
@@ -20,12 +21,14 @@ import { StepLogModule } from "../runtime/step-log.js";
20
21
  import { SubagentsModule } from "../runtime/subagents/subagents.service.js";
21
22
  import { UsageReportModule } from "../runtime/usage-report.js";
22
23
  import { SandboxModule } from "../sandbox/sandbox.module.js";
24
+ import { ScheduledTaskStoreModule } from "../schedules/store.module.js";
23
25
  import { SkillsModule } from "../skills/skills.module.js";
24
26
  import { StorageModule } from "../storage/storage.module.js";
25
27
  import { AdminConnectors } from "./admin-connectors.js";
26
28
  import { ApprovalsService } from "./approvals.service.js";
27
29
  import { Breaker } from "./breaker.js";
28
30
  import { BuiltinTools } from "./builtins/builtin-tools.js";
31
+ import { ScheduleToolsPort, ScheduleUpdates } from "./builtins/schedules.js";
29
32
  import { WebPages } from "./builtins/web/page-cache.js";
30
33
  import { ConnectorAccountPolicies } from "./connector-account-policies.js";
31
34
  import { ConnectorContent } from "./connector-content.js";
@@ -63,6 +66,8 @@ ToolsModule = __decorate([
63
66
  // the builtin sandbox and skill tools run in the conversation's device
64
67
  SandboxModule,
65
68
  DevicesModule,
69
+ // 「这台设备一直允许」 changes the device's local policy (network asks)
70
+ LocalDevicesModule,
66
71
  // `Bash` with run_in_background records its task; TaskOutput / TaskStop finish and stop it
67
72
  BackgroundModule,
68
73
  // the conversation page's tool metadata reads only what the viewer may see
@@ -74,11 +79,17 @@ ToolsModule = __decorate([
74
79
  // the `Platform` tool's operations, actor facts and agent docs (imports no domain module)
75
80
  PlatformModule,
76
81
  QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
82
+ // a trial run's approvals become its scheduled task's grants (scheduled tasks spec §7)
83
+ ScheduledTaskStoreModule,
77
84
  ],
78
85
  providers: [
79
86
  Credentials,
80
87
  ToolOutputs,
81
88
  BuiltinTools,
89
+ // `ScheduleUpdate`'s write in one transaction (scheduled tasks spec §11)
90
+ ScheduleUpdates,
91
+ // where `SchedulesModule` binds the task service for the schedule tools (no provider cycle)
92
+ ScheduleToolsPort,
82
93
  WebPages,
83
94
  ToolRegistry,
84
95
  ApprovalsService,
@@ -96,6 +107,7 @@ ToolsModule = __decorate([
96
107
  OrgToolSwitches,
97
108
  ],
98
109
  exports: [
110
+ ScheduleToolsPort,
99
111
  Credentials,
100
112
  ToolOutputs,
101
113
  BuiltinTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.21",
3
+ "version": "0.1.0-rc.22",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,7 +65,7 @@
65
65
  "yaml": "^2.9.1",
66
66
  "yauzl": "3.4.0",
67
67
  "zod": "^4.6.7",
68
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.15"
68
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.16"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",