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

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 (293) hide show
  1. package/bootstrap.json +25 -25
  2. package/dist/admin/admin.module.d.ts +2 -1
  3. package/dist/admin/admin.module.js +9 -3
  4. package/dist/admin/health.service.d.ts +2 -2
  5. package/dist/admin/health.service.js +6 -2
  6. package/dist/admin/outbox-dead.controller.d.ts +23 -0
  7. package/dist/admin/outbox-dead.controller.js +62 -0
  8. package/dist/admin/outbox-dead.schemas.d.ts +8 -0
  9. package/dist/admin/outbox-dead.schemas.js +18 -0
  10. package/dist/admin/outbox-dead.service.d.ts +66 -0
  11. package/dist/admin/outbox-dead.service.js +179 -0
  12. package/dist/admin/overview.service.d.ts +40 -5
  13. package/dist/admin/overview.service.js +41 -7
  14. package/dist/agents/agents.module.js +3 -0
  15. package/dist/agents/agents.service.d.ts +6 -1
  16. package/dist/agents/agents.service.js +11 -2
  17. package/dist/agents/from-template.js +1 -1
  18. package/dist/background/finish.d.ts +5 -2
  19. package/dist/background/finish.js +14 -3
  20. package/dist/background/launch.d.ts +2 -1
  21. package/dist/background/launch.js +5 -2
  22. package/dist/background/store.d.ts +8 -0
  23. package/dist/background/store.js +24 -0
  24. package/dist/catalog/admin-catalog.controller.d.ts +21 -2
  25. package/dist/catalog/admin-catalog.controller.js +45 -6
  26. package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
  27. package/dist/catalog/admin-catalog.schemas.js +8 -0
  28. package/dist/catalog/catalog-import.module.js +0 -4
  29. package/dist/catalog/catalog-scope.service.d.ts +63 -26
  30. package/dist/catalog/catalog-scope.service.js +146 -71
  31. package/dist/catalog/catalog.module.d.ts +2 -1
  32. package/dist/catalog/catalog.module.js +9 -4
  33. package/dist/catalog/catalog.service.d.ts +11 -4
  34. package/dist/catalog/catalog.service.js +21 -5
  35. package/dist/catalog/cli.d.ts +4 -3
  36. package/dist/catalog/cli.js +8 -13
  37. package/dist/catalog/job-ids.d.ts +8 -20
  38. package/dist/catalog/job-ids.js +11 -16
  39. package/dist/catalog/marketplaces.service.d.ts +1 -1
  40. package/dist/catalog/org-plugin-uploads.d.ts +11 -4
  41. package/dist/catalog/org-plugin-uploads.js +21 -6
  42. package/dist/catalog/owned-items.d.ts +6 -13
  43. package/dist/catalog/owned-items.js +11 -29
  44. package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
  45. package/dist/catalog/owned-plugin-cleanup.js +12 -66
  46. package/dist/catalog/platform-components.d.ts +56 -34
  47. package/dist/catalog/platform-components.js +68 -78
  48. package/dist/catalog/platform-import-update.d.ts +50 -0
  49. package/dist/catalog/platform-import-update.js +116 -0
  50. package/dist/catalog/plugin-import.d.ts +64 -26
  51. package/dist/catalog/plugin-import.js +113 -77
  52. package/dist/catalog/plugin-upgrade.d.ts +22 -4
  53. package/dist/catalog/plugin-upgrade.js +26 -19
  54. package/dist/catalog/scope-jobs.d.ts +59 -0
  55. package/dist/catalog/scope-jobs.js +65 -0
  56. package/dist/catalog/scope-sync-workers.d.ts +22 -0
  57. package/dist/catalog/scope-sync-workers.js +58 -2
  58. package/dist/catalog/scope-sync.d.ts +40 -65
  59. package/dist/catalog/scope-sync.js +114 -122
  60. package/dist/channels/channels.module.d.ts +5 -2
  61. package/dist/channels/channels.module.js +9 -2
  62. package/dist/channels/deliver-job.d.ts +25 -0
  63. package/dist/channels/deliver-job.js +25 -0
  64. package/dist/channels/inbound.d.ts +1 -1
  65. package/dist/channels/inbound.js +4 -17
  66. package/dist/channels/jobs.d.ts +28 -18
  67. package/dist/channels/jobs.js +63 -26
  68. package/dist/channels/threads.d.ts +4 -3
  69. package/dist/channels/threads.js +4 -3
  70. package/dist/config/schema.d.ts +0 -3
  71. package/dist/config/schema.js +11 -20
  72. package/dist/conversations/agent-tree.d.ts +16 -0
  73. package/dist/conversations/agent-tree.js +36 -0
  74. package/dist/conversations/conversations.controller.js +4 -2
  75. package/dist/conversations/conversations.service.d.ts +61 -19
  76. package/dist/conversations/conversations.service.js +69 -43
  77. package/dist/conversations/event-log.d.ts +7 -10
  78. package/dist/conversations/event-log.js +24 -15
  79. package/dist/conversations/events.d.ts +21 -0
  80. package/dist/conversations/events.js +26 -2
  81. package/dist/conversations/queue.service.d.ts +8 -3
  82. package/dist/conversations/queue.service.js +37 -13
  83. package/dist/conversations/source.d.ts +10 -0
  84. package/dist/conversations/source.js +17 -0
  85. package/dist/conversations/titles.d.ts +21 -8
  86. package/dist/conversations/titles.js +42 -15
  87. package/dist/conversations/tree.d.ts +10 -0
  88. package/dist/conversations/tree.js +35 -0
  89. package/dist/db/kernel-schema.d.ts +35 -22
  90. package/dist/devices/devices.service.d.ts +23 -11
  91. package/dist/devices/devices.service.js +34 -26
  92. package/dist/errors.d.ts +11 -0
  93. package/dist/errors.js +11 -0
  94. package/dist/evals/run-replay.d.ts +13 -2
  95. package/dist/evals/run-replay.js +42 -9
  96. package/dist/evals/snapshot.d.ts +7 -1
  97. package/dist/evals/snapshot.js +2 -1
  98. package/dist/extensions/extensions.module.js +3 -0
  99. package/dist/extensions/hooks.d.ts +22 -3
  100. package/dist/extensions/hooks.js +15 -1
  101. package/dist/extensions/lifecycle-hooks.d.ts +99 -0
  102. package/dist/extensions/lifecycle-hooks.js +273 -0
  103. package/dist/extensions/lifecycle.d.ts +6 -1
  104. package/dist/extensions/lifecycle.js +9 -2
  105. package/dist/extensions/org-config.d.ts +13 -1
  106. package/dist/extensions/org-config.js +19 -2
  107. package/dist/extensions/permission-view.d.ts +9 -5
  108. package/dist/extensions/permission-view.js +9 -4
  109. package/dist/extensions/point-defaults.d.ts +28 -1
  110. package/dist/extensions/point-defaults.js +44 -13
  111. package/dist/extensions/usage-subscribers.d.ts +43 -0
  112. package/dist/extensions/usage-subscribers.js +108 -0
  113. package/dist/http/api.module.js +2 -0
  114. package/dist/inbox/inbox.service.d.ts +6 -20
  115. package/dist/inbox/inbox.service.js +0 -3
  116. package/dist/inbox/org-notices.d.ts +1 -1
  117. package/dist/inbox/platform-inbox.service.d.ts +3 -32
  118. package/dist/inbox/platform-inbox.service.js +5 -54
  119. package/dist/index.d.ts +1 -0
  120. package/dist/index.js +1 -0
  121. package/dist/infra/cli/kernel-cli.module.js +5 -0
  122. package/dist/infra/events/kernel-events.d.ts +9 -35
  123. package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
  124. package/dist/infra/outbox/outbox-relay.module.js +18 -0
  125. package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
  126. package/dist/infra/outbox/outbox.dispatcher.js +310 -0
  127. package/dist/infra/outbox/outbox.module.d.ts +24 -0
  128. package/dist/infra/outbox/outbox.module.js +50 -0
  129. package/dist/infra/outbox/outbox.relay.d.ts +31 -0
  130. package/dist/infra/outbox/outbox.relay.js +76 -0
  131. package/dist/infra/outbox/outbox.service.d.ts +51 -0
  132. package/dist/infra/outbox/outbox.service.js +131 -0
  133. package/dist/infra/outbox/registry.d.ts +244 -0
  134. package/dist/infra/outbox/registry.js +234 -0
  135. package/dist/infra/queue/queue-names.d.ts +8 -0
  136. package/dist/infra/queue/queue-names.js +17 -0
  137. package/dist/kernel.module.js +3 -0
  138. package/dist/local-devices/device-policy.service.d.ts +4 -0
  139. package/dist/local-devices/device-policy.service.js +61 -3
  140. package/dist/local-devices/machines.d.ts +13 -5
  141. package/dist/local-devices/machines.js +45 -7
  142. package/dist/local-devices/policy-summary.d.ts +44 -1
  143. package/dist/local-devices/policy-summary.js +69 -0
  144. package/dist/models/embeddings.d.ts +9 -9
  145. package/dist/models/embeddings.js +11 -18
  146. package/dist/models/models.module.js +3 -0
  147. package/dist/orgs/directory-sync.js +1 -1
  148. package/dist/orgs/members.service.js +1 -1
  149. package/dist/orgs/offboarding.d.ts +50 -57
  150. package/dist/orgs/offboarding.js +117 -162
  151. package/dist/orgs/orgs.module.js +3 -0
  152. package/dist/orgs/orgs.service.d.ts +11 -5
  153. package/dist/orgs/orgs.service.js +22 -6
  154. package/dist/overrides.d.ts +23 -3
  155. package/dist/overrides.js +12 -2
  156. package/dist/platform/exclusions.js +5 -0
  157. package/dist/platform/ops/admin.ops.js +18 -2
  158. package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
  159. package/dist/platform/ops/catalog-admin.ops.js +32 -4
  160. package/dist/platform/ops/inbox.ops.js +4 -20
  161. package/dist/platform/ops/outbox.ops.d.ts +21 -0
  162. package/dist/platform/ops/outbox.ops.js +125 -0
  163. package/dist/platform/ops/platform.ops.d.ts +1 -0
  164. package/dist/platform/ops/platform.ops.js +6 -3
  165. package/dist/platform/ops/schedules.ops.js +40 -1
  166. package/dist/platform/platform-ops.module.d.ts +2 -1
  167. package/dist/platform/platform-ops.module.js +3 -0
  168. package/dist/platform/route-schemas.js +3 -0
  169. package/dist/roles/worker-role.d.ts +10 -5
  170. package/dist/roles/worker-role.js +19 -9
  171. package/dist/roles/worker.module.js +11 -8
  172. package/dist/runtime/context/build.d.ts +3 -0
  173. package/dist/runtime/context/build.js +15 -0
  174. package/dist/runtime/context/plugin-context.d.ts +20 -0
  175. package/dist/runtime/context/plugin-context.js +30 -0
  176. package/dist/runtime/langgraph-loop.js +17 -5
  177. package/dist/runtime/limits.d.ts +15 -0
  178. package/dist/runtime/limits.js +25 -0
  179. package/dist/runtime/loop.d.ts +16 -3
  180. package/dist/runtime/loop.js +2 -1
  181. package/dist/runtime/maintenance.d.ts +7 -3
  182. package/dist/runtime/maintenance.js +52 -12
  183. package/dist/runtime/model-call.d.ts +20 -17
  184. package/dist/runtime/model-call.js +15 -24
  185. package/dist/runtime/plugin-context.service.d.ts +69 -0
  186. package/dist/runtime/plugin-context.service.js +208 -0
  187. package/dist/runtime/queue-lost-jobs.d.ts +24 -0
  188. package/dist/runtime/queue-lost-jobs.js +51 -0
  189. package/dist/runtime/run-control.d.ts +47 -5
  190. package/dist/runtime/run-control.js +79 -12
  191. package/dist/runtime/run-finish.d.ts +39 -12
  192. package/dist/runtime/run-finish.js +97 -14
  193. package/dist/runtime/run-jobs.d.ts +95 -13
  194. package/dist/runtime/run-jobs.js +199 -28
  195. package/dist/runtime/run-orphan-check.job.d.ts +142 -0
  196. package/dist/runtime/run-orphan-check.job.js +339 -0
  197. package/dist/runtime/run-processors.d.ts +16 -9
  198. package/dist/runtime/run-processors.js +1 -1
  199. package/dist/runtime/run-queue.d.ts +75 -53
  200. package/dist/runtime/run-queue.js +189 -263
  201. package/dist/runtime/run-steps.d.ts +1 -2
  202. package/dist/runtime/run-steps.js +8 -11
  203. package/dist/runtime/run-worker.d.ts +26 -16
  204. package/dist/runtime/run-worker.js +132 -37
  205. package/dist/runtime/runtime.module.d.ts +2 -3
  206. package/dist/runtime/runtime.module.js +7 -6
  207. package/dist/runtime/step-log.d.ts +16 -4
  208. package/dist/runtime/step-log.js +26 -6
  209. package/dist/runtime/stream.controller.d.ts +2 -4
  210. package/dist/runtime/stream.controller.js +5 -11
  211. package/dist/runtime/subagents/child.js +1 -1
  212. package/dist/runtime/subagents/definition.d.ts +2 -1
  213. package/dist/runtime/subagents/definition.js +4 -2
  214. package/dist/runtime/subagents/deliveries.d.ts +6 -3
  215. package/dist/runtime/subagents/deliveries.js +16 -7
  216. package/dist/runtime/subagents/dispatch.d.ts +12 -12
  217. package/dist/runtime/subagents/dispatch.js +14 -38
  218. package/dist/runtime/subagents/messages.d.ts +5 -1
  219. package/dist/runtime/subagents/messages.js +11 -3
  220. package/dist/runtime/subagents/skill-fork.d.ts +3 -3
  221. package/dist/runtime/subagents/skill-fork.js +8 -5
  222. package/dist/runtime/subagents/subagents.service.js +1 -1
  223. package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
  224. package/dist/runtime/subagents/tree-jobs.js +86 -0
  225. package/dist/runtime/usage-report-jobs.d.ts +23 -48
  226. package/dist/runtime/usage-report-jobs.js +67 -119
  227. package/dist/runtime/usage-report.d.ts +28 -77
  228. package/dist/runtime/usage-report.js +35 -213
  229. package/dist/runtime/waits.d.ts +48 -198
  230. package/dist/runtime/waits.js +103 -805
  231. package/dist/sandbox/jobs.d.ts +20 -10
  232. package/dist/sandbox/jobs.js +23 -17
  233. package/dist/sandbox/sandbox.service.d.ts +17 -4
  234. package/dist/sandbox/sandbox.service.js +55 -14
  235. package/dist/schedules/calendar.d.ts +134 -0
  236. package/dist/schedules/calendar.js +410 -0
  237. package/dist/schedules/fire.d.ts +26 -7
  238. package/dist/schedules/fire.js +91 -22
  239. package/dist/schedules/job-ids.d.ts +66 -17
  240. package/dist/schedules/job-ids.js +81 -13
  241. package/dist/schedules/run-tracker.d.ts +0 -5
  242. package/dist/schedules/run-tracker.js +2 -14
  243. package/dist/schedules/scan.job.d.ts +13 -32
  244. package/dist/schedules/scan.job.js +50 -164
  245. package/dist/schedules/schedule-math.d.ts +42 -0
  246. package/dist/schedules/schedule-math.js +52 -0
  247. package/dist/schedules/scheduled-tasks.service.d.ts +22 -6
  248. package/dist/schedules/scheduled-tasks.service.js +70 -14
  249. package/dist/schedules/schedules.controller.d.ts +5 -1
  250. package/dist/schedules/schedules.controller.js +15 -1
  251. package/dist/schedules/schedules.module.js +0 -4
  252. package/dist/schedules/schedules.schemas.d.ts +35 -0
  253. package/dist/schedules/schedules.schemas.js +27 -0
  254. package/dist/schedules/store.d.ts +5 -0
  255. package/dist/schedules/store.js +20 -0
  256. package/dist/skills/changes.controller.d.ts +1 -1
  257. package/dist/skills/changes.controller.js +1 -1
  258. package/dist/tools/approvals.service.d.ts +41 -11
  259. package/dist/tools/approvals.service.js +108 -14
  260. package/dist/tools/builtin-names.d.ts +5 -3
  261. package/dist/tools/builtin-names.js +12 -4
  262. package/dist/tools/builtins/builtin-tools.d.ts +1 -2
  263. package/dist/tools/builtins/builtin-tools.js +21 -17
  264. package/dist/tools/builtins/sandbox/bash.js +9 -3
  265. package/dist/tools/builtins/sandbox/common.d.ts +18 -1
  266. package/dist/tools/builtins/sandbox/common.js +14 -1
  267. package/dist/tools/builtins/sandbox/index.d.ts +2 -1
  268. package/dist/tools/builtins/sandbox/index.js +4 -1
  269. package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
  270. package/dist/tools/builtins/sandbox/task-list.js +130 -0
  271. package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
  272. package/dist/tools/builtins/sandbox/task-output.js +21 -3
  273. package/dist/tools/builtins/web/extract.d.ts +2 -4
  274. package/dist/tools/builtins/web/extract.js +2 -5
  275. package/dist/tools/pipeline.d.ts +12 -3
  276. package/dist/tools/pipeline.js +6 -1
  277. package/dist/tools/questions.service.d.ts +30 -10
  278. package/dist/tools/questions.service.js +41 -27
  279. package/dist/tools/registry.d.ts +12 -3
  280. package/dist/tools/registry.js +13 -3
  281. package/dist/tools/tools.module.js +4 -3
  282. package/package.json +3 -3
  283. package/src/migrations/0118_device_policy_ops.sql +6 -0
  284. package/src/migrations/0119_outbox.sql +22 -0
  285. package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
  286. package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
  287. package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
  288. package/src/migrations/0123_pgvector.sql +27 -0
  289. package/src/migrations/0124_conversation_source.sql +36 -0
  290. package/dist/catalog/platform-migration.d.ts +0 -115
  291. package/dist/catalog/platform-migration.js +0 -583
  292. package/dist/infra/queue/add-job-once.d.ts +0 -14
  293. package/dist/infra/queue/add-job-once.js +0 -16
@@ -13,6 +13,7 @@ import { Inject, Injectable } from "@nestjs/common";
13
13
  import { sql } from "kysely";
14
14
  import { EventLog } from "../conversations/event-log.js";
15
15
  import { NETWORK_ASK_MAX_HOSTS, NETWORK_HOST_MAX_CHARS, } from "../conversations/events.js";
16
+ import { conversationSourceOf } from "../conversations/source.js";
16
17
  import { ApiError } from "../errors.js";
17
18
  import { audit } from "../identity/audit.js";
18
19
  import { KernelConfigService } from "../infra/config/kernel-config.service.js";
@@ -24,6 +25,7 @@ import { LockService } from "../infra/lock/lock.service.js";
24
25
  import { WithLock } from "../infra/lock/with-lock.decorator.js";
25
26
  import { DevicePolicyService } from "../local-devices/device-policy.service.js";
26
27
  import { ORG_ROLES, roleAtLeast } from "../orgs/roles.js";
28
+ import { RunResume } from "../runtime/run-jobs.js";
27
29
  import { rootConversationOf } from "../runtime/subagents/child.js";
28
30
  import { SandboxService } from "../sandbox/sandbox.service.js";
29
31
  import { readScheduleSettings } from "../schedules/effective-settings.js";
@@ -102,10 +104,12 @@ function canHandle(a, initiator, accountId, role) {
102
104
  * 工具调用审批:新建(loop 调用)、列出 / 查看 / 处理(HTTP),以及「以后都允许」授权的查看与撤销。
103
105
  * 状态全部在 PG:处理决定在事务里 `SELECT … FOR UPDATE` 审批行,多副本并发处理同一审批时恰好一个生效。
104
106
  *
105
- * A decision is announced after the commit as the domain event `approval.decided` (exactly once:
106
- * only the decision that changed the row from pending), which the run's waits follow to resume it
107
- * (spec §3b; the wait sweep resumes a run whose resume was lost). Events are appended in the
108
- * context's transaction (`EventLog.appendIn`) and published after the commit.
107
+ * A decision resumes its waiting run in the decision's transaction (`RunResume.resumeIn`, after the
108
+ * decided row: lock order approval, then run; transactional outbox spec §6). It is announced after
109
+ * the commit as the domain event `approval.decided` (exactly once: only the decision that changed
110
+ * the row from pending; no kernel subscriber: the IM card update is owed by the `approval_decided`
111
+ * append, transactional outbox spec §8.1). Events are appended in the context's transaction
112
+ * (`EventLog.appendIn`) and published after the commit.
109
113
  */
110
114
  let ApprovalsService = class ApprovalsService {
111
115
  db;
@@ -118,6 +122,7 @@ let ApprovalsService = class ApprovalsService {
118
122
  policies;
119
123
  lockService;
120
124
  sandbox;
125
+ resumes;
121
126
  constructor(db, config, eventLog, events, txHost, schedules,
122
127
  /** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
123
128
  registry,
@@ -126,7 +131,9 @@ let ApprovalsService = class ApprovalsService {
126
131
  /** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
127
132
  lockService,
128
133
  /** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
129
- sandbox) {
134
+ sandbox,
135
+ /** A decision resumes its waiting run in the decision's transaction. */
136
+ resumes) {
130
137
  this.db = db;
131
138
  this.config = config;
132
139
  this.eventLog = eventLog;
@@ -137,6 +144,7 @@ let ApprovalsService = class ApprovalsService {
137
144
  this.policies = policies;
138
145
  this.lockService = lockService;
139
146
  this.sandbox = sandbox;
147
+ this.resumes = resumes;
140
148
  }
141
149
  /**
142
150
  * 新建 pending 审批(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `approval_requested`,同一事务。
@@ -549,7 +557,7 @@ let ApprovalsService = class ApprovalsService {
549
557
  /** After the commit: the decision's events reach the page, the run's waits and IM delivery. */
550
558
  async announceDecided(record, toSeq) {
551
559
  await this.eventLog.notify(record.conversationId, toSeq);
552
- // waits resume the run and its IM delivery is enqueued (handler failures are logged)
560
+ // its IM delivery is enqueued (handler failures are logged); the run resumed in the decision
553
561
  await this.events.emit("approval.decided", {
554
562
  id: record.id,
555
563
  orgId: record.orgId,
@@ -571,6 +579,9 @@ let ApprovalsService = class ApprovalsService {
571
579
  // decision committed, even when a caller has a transaction open
572
580
  async decideIn(orgId, id, accountId, body, actorTokenId, network) {
573
581
  const t = this.db.db;
582
+ // lock order: the conversation, the approval, then the run (as a stop of the waiting run)
583
+ if (!(await this.lockConversationOf(id, orgId)))
584
+ throw notFound();
574
585
  const locked = await t
575
586
  .selectFrom("approvals")
576
587
  .selectAll()
@@ -734,6 +745,7 @@ let ApprovalsService = class ApprovalsService {
734
745
  ...(reason !== undefined && { reason }),
735
746
  },
736
747
  });
748
+ await this.resumes.resumeIn(before.runId, id);
737
749
  return { record: toRecord(updated), toSeq: Math.max(...written.map((e) => e.seq)) };
738
750
  }
739
751
  await audit(t, {
@@ -754,6 +766,9 @@ let ApprovalsService = class ApprovalsService {
754
766
  ...(reason !== undefined && { reason }),
755
767
  },
756
768
  });
769
+ // the run last (lock order: the decided row, then `runs`); a still-running run is resumed by
770
+ // its suspend's re-check
771
+ await this.resumes.resumeIn(before.runId, id);
757
772
  return { record: toRecord(updated), toSeq: Math.max(...written.map((e) => e.seq)) };
758
773
  }
759
774
  /**
@@ -800,6 +815,8 @@ let ApprovalsService = class ApprovalsService {
800
815
  disabledTools: def?.disabledTools ?? [],
801
816
  subagentTools: def?.subagentTools,
802
817
  subagents: def?.subagents,
818
+ // the tools of the trial run's conversation, where the approval was asked
819
+ source: await conversationSourceOf(t, a.conversationId),
803
820
  });
804
821
  const tool = tools.find((x) => x.key === a.toolKey);
805
822
  const { forbiddenTools } = await readScheduleSettings(t, this.config, a.orgId);
@@ -908,6 +925,8 @@ let ApprovalsService = class ApprovalsService {
908
925
  }
909
926
  async expireIn(id) {
910
927
  const t = this.db.db;
928
+ if (!(await this.lockConversationOf(id)))
929
+ return { outcome: "settled" };
911
930
  const locked = await t
912
931
  .selectFrom("approvals")
913
932
  .selectAll()
@@ -959,16 +978,41 @@ let ApprovalsService = class ApprovalsService {
959
978
  return out;
960
979
  }
961
980
  /**
962
- * 在调用方的事务内(`mandatory`)取消某组织里指定由 `accountId` 处理(`requested_for`)的全部 pending 审批(成员离职),
963
- * 走与 {@link cancelPendingIn} 相同的取消路径:置 cancelled、写 `approval_decided{cancelled}`(处理人为 null)。
964
- * 返回被取消的记录与各会话写到的最大 seq;调用方提交后发布事件(并可续跑对应运行)。
981
+ * The conversations of `orgId` holding a pending approval `accountId` is asked to decide
982
+ * (`requested_for`), read without locks: offboarding locks them, by tree root first
983
+ * (`lockConversationTrees`), before {@link cancelPendingFor}.
965
984
  */
966
- async cancelPendingFor(orgId, accountId) {
985
+ async pendingConversationsFor(orgId, accountId) {
986
+ const rows = await this.db
987
+ .selectFrom("approvals")
988
+ .select("conversation_id")
989
+ .distinct()
990
+ .where("org_id", "=", orgId)
991
+ .where("requested_for", "=", accountId)
992
+ .where("status", "=", "pending")
993
+ .execute();
994
+ return rows.map((r) => r.conversation_id);
995
+ }
996
+ /**
997
+ * In the caller's transaction (`mandatory`; offboarding): cancels every pending approval of
998
+ * `orgId` that `accountId` is asked to decide (`requested_for`) in `conversationIds`, the
999
+ * conversations the caller already locked (by tree root first, `lockConversationTrees`; lock
1000
+ * order: conversations, then approval rows), along the same path as {@link cancelPendingIn}:
1001
+ * `cancelled`, `approval_decided{cancelled}` with no decider. An approval asked meanwhile in
1002
+ * another conversation is left to its run's stop, so no conversation is locked after a row.
1003
+ * Returns the cancelled records and the highest seq written per conversation; the caller
1004
+ * publishes after the commit.
1005
+ */
1006
+ async cancelPendingFor(orgId, accountId, conversationIds) {
967
1007
  const t = this.db.db;
1008
+ const convs = [...conversationIds];
1009
+ if (convs.length === 0)
1010
+ return { records: [], seqs: new Map() };
968
1011
  const rows = await t
969
1012
  .selectFrom("approvals")
970
1013
  .selectAll()
971
1014
  .where("org_id", "=", orgId)
1015
+ .where("conversation_id", "in", convs)
972
1016
  .where("requested_for", "=", accountId)
973
1017
  .where("status", "=", "pending")
974
1018
  .orderBy("created_at")
@@ -996,17 +1040,29 @@ let ApprovalsService = class ApprovalsService {
996
1040
  * active runs `authorAccountId` started, in the conversation and in its sub-agents'
997
1041
  * conversations at any depth (their asks are shown in the root). Each becomes `skipped` with
998
1042
  * `approval_decided{skipped}` by the author; access stays blocked. Returns the skipped records and
999
- * the highest seq written per conversation; the caller notifies and emits
1000
- * `approval.decided{skipped}` after the commit, so the runs resume.
1043
+ * the highest seq written per conversation; the caller resumes their waiting runs in this
1044
+ * transaction once every skipped row is locked (`ConversationsService.skipWaitsIn`), and notifies
1045
+ * and emits the domain event `approval.decided{skipped}` after the commit (its IM card update is
1046
+ * owed by the append, transactional outbox spec §8.1).
1001
1047
  */
1002
1048
  async skipNetworkIn(conversationId, authorAccountId) {
1003
1049
  const t = this.db.db;
1004
1050
  const tree = await this.subtreeOf(conversationId);
1051
+ // the sub-agents' conversations with a pending ask first, in id order (lock order:
1052
+ // conversation, then approval rows; the caller holds `conversationId`'s already)
1053
+ const convs = await this.lockConversations(t
1054
+ .selectFrom("approvals")
1055
+ .select("conversation_id")
1056
+ .where("conversation_id", "in", tree)
1057
+ .where("kind", "=", "network")
1058
+ .where("status", "=", "pending"));
1059
+ if (convs.length === 0)
1060
+ return { records: [], seqs: new Map() };
1005
1061
  const rows = await t
1006
1062
  .selectFrom("approvals as a")
1007
1063
  .innerJoin("runs as r", "r.id", "a.run_id")
1008
1064
  .selectAll("a")
1009
- .where("a.conversation_id", "in", tree)
1065
+ .where("a.conversation_id", "in", convs)
1010
1066
  .where("a.kind", "=", "network")
1011
1067
  .where("a.status", "=", "pending")
1012
1068
  .where(sql `a.expires_at > now()`)
@@ -1031,6 +1087,43 @@ let ApprovalsService = class ApprovalsService {
1031
1087
  }
1032
1088
  return { records, seqs };
1033
1089
  }
1090
+ /**
1091
+ * Locks the conversation row of approval `id` (`SELECT … FOR UPDATE`; re-entrant with the lock
1092
+ * `EventLog.appendIn` takes later). Every write to an approval row takes its conversation's lock
1093
+ * first, then the approval row, then the run row, as the stop of a waiting run
1094
+ * (`Waits.abortWaitingIn`) and the questions do, so no two of them can deadlock. False: no such
1095
+ * approval (in `orgId`).
1096
+ */
1097
+ async lockConversationOf(id, orgId) {
1098
+ const t = this.db.db;
1099
+ let q = t.selectFrom("approvals").select("conversation_id").where("id", "=", id);
1100
+ if (orgId !== undefined)
1101
+ q = q.where("org_id", "=", orgId);
1102
+ const row = await q.executeTakeFirst();
1103
+ if (!row)
1104
+ return false;
1105
+ await t
1106
+ .selectFrom("conversations")
1107
+ .select("id")
1108
+ .where("id", "=", row.conversation_id)
1109
+ .forUpdate()
1110
+ .executeTakeFirst();
1111
+ return true;
1112
+ }
1113
+ /** Locks, in id order, the conversations `ids` selects; returns their ids. */
1114
+ async lockConversations(ids) {
1115
+ const found = [...new Set((await ids.execute()).map((r) => r.conversation_id))];
1116
+ if (found.length === 0)
1117
+ return [];
1118
+ const rows = await this.db.db
1119
+ .selectFrom("conversations")
1120
+ .select("id")
1121
+ .where("id", "in", found)
1122
+ .orderBy("id")
1123
+ .forUpdate()
1124
+ .execute();
1125
+ return rows.map((r) => r.id);
1126
+ }
1034
1127
  /** `conversationId` and its sub-agents' conversations at any depth (at most 16 levels). */
1035
1128
  async subtreeOf(conversationId) {
1036
1129
  const all = [conversationId];
@@ -1252,6 +1345,7 @@ ApprovalsService = __decorate([
1252
1345
  __param(6, Inject(ToolRegistry)),
1253
1346
  __param(7, Inject(DevicePolicyService)),
1254
1347
  __param(8, Inject(LockService)),
1255
- __param(9, Inject(SandboxService))
1348
+ __param(9, Inject(SandboxService)),
1349
+ __param(10, Inject(RunResume))
1256
1350
  ], ApprovalsService);
1257
1351
  export { ApprovalsService };
@@ -23,6 +23,7 @@ export declare const BUILTIN: {
23
23
  readonly SubagentHandback: "SubagentHandback";
24
24
  readonly TaskOutput: "TaskOutput";
25
25
  readonly TaskStop: "TaskStop";
26
+ readonly TaskList: "TaskList";
26
27
  readonly WebSearch: "WebSearch";
27
28
  readonly WebFetch: "WebFetch";
28
29
  readonly Platform: "Platform";
@@ -35,7 +36,8 @@ export declare const BUILTIN: {
35
36
  export type BuiltinName = (typeof BUILTIN)[keyof typeof BUILTIN];
36
37
  /**
37
38
  * The builtin tools that run in the conversation's sandbox. `TaskOutput` and `TaskStop` read and
38
- * stop the background tasks `Bash` started (background Bash spec §2.2, §2.3).
39
+ * stop the background tasks `Bash` started (background Bash spec §2.2, §2.3); `TaskList` lists them
40
+ * (and the conversation's sub-agents).
39
41
  */
40
42
  export declare const SANDBOX_TOOL_NAMES: readonly BuiltinName[];
41
43
  /** The builtin tools that reach the web through the org's web-search / web-fetch providers. */
@@ -47,7 +49,7 @@ export declare const WEB_TOOL_NAMES: readonly BuiltinName[];
47
49
  export declare const BASH_COMPANION_TOOLS: readonly BuiltinName[];
48
50
  /**
49
51
  * Sandbox tools that need approval when the org setting `sandboxToolPolicy` is `ask` (not the
50
- * read-only ones). Not `TaskOutput` or `TaskStop`: they only read or stop a task whose launch
52
+ * read-only ones). Not `TaskOutput`, `TaskStop` or `TaskList`: they only read, list or stop tasks whose launch
51
53
  * already went through `Bash`'s policy (background Bash spec §4.5).
52
54
  */
53
55
  export declare const SANDBOX_ASK_TOOL_NAMES: ReadonlySet<string>;
@@ -110,7 +112,7 @@ export declare const CHILD_REMOVED_TOOLS: readonly ["AskUserQuestion", "Deliver"
110
112
  /**
111
113
  * The builtins every agent has unless it turns them off (tools default-on spec §4), in this
112
114
  * order: {@link BUILTIN_TOOL_NAMES} without the ones the registry manages by rule (`ToolSearch`,
113
- * `Skill`, `SendMessage`, `SubagentHandback`) and without `TaskOutput` / `TaskStop`, which come
115
+ * `Skill`, `SendMessage`, `SubagentHandback`) and without `TaskOutput` / `TaskStop` / `TaskList`, which come
114
116
  * with `Bash` ({@link BASH_COMPANION_TOOLS}). These are also the builtins an agent can turn off.
115
117
  */
116
118
  export declare const AGENT_BUILTIN_NAMES: readonly BuiltinName[];
@@ -23,6 +23,7 @@ export const BUILTIN = {
23
23
  SubagentHandback: "SubagentHandback",
24
24
  TaskOutput: "TaskOutput",
25
25
  TaskStop: "TaskStop",
26
+ TaskList: "TaskList",
26
27
  WebSearch: "WebSearch",
27
28
  WebFetch: "WebFetch",
28
29
  Platform: "Platform",
@@ -34,7 +35,8 @@ export const BUILTIN = {
34
35
  };
35
36
  /**
36
37
  * The builtin tools that run in the conversation's sandbox. `TaskOutput` and `TaskStop` read and
37
- * stop the background tasks `Bash` started (background Bash spec §2.2, §2.3).
38
+ * stop the background tasks `Bash` started (background Bash spec §2.2, §2.3); `TaskList` lists them
39
+ * (and the conversation's sub-agents).
38
40
  */
39
41
  export const SANDBOX_TOOL_NAMES = [
40
42
  BUILTIN.Bash,
@@ -46,6 +48,7 @@ export const SANDBOX_TOOL_NAMES = [
46
48
  BUILTIN.Deliver,
47
49
  BUILTIN.TaskOutput,
48
50
  BUILTIN.TaskStop,
51
+ BUILTIN.TaskList,
49
52
  ];
50
53
  /** The builtin tools that reach the web through the org's web-search / web-fetch providers. */
51
54
  export const WEB_TOOL_NAMES = [BUILTIN.WebSearch, BUILTIN.WebFetch];
@@ -53,10 +56,14 @@ export const WEB_TOOL_NAMES = [BUILTIN.WebSearch, BUILTIN.WebFetch];
53
56
  * The tools offered with `Bash` whenever it is offered (background Bash: a model that can start a
54
57
  * background task can read and stop it). `ToolRegistry` adds them; an explicit ref is harmless.
55
58
  */
56
- export const BASH_COMPANION_TOOLS = [BUILTIN.TaskOutput, BUILTIN.TaskStop];
59
+ export const BASH_COMPANION_TOOLS = [
60
+ BUILTIN.TaskOutput,
61
+ BUILTIN.TaskStop,
62
+ BUILTIN.TaskList,
63
+ ];
57
64
  /**
58
65
  * Sandbox tools that need approval when the org setting `sandboxToolPolicy` is `ask` (not the
59
- * read-only ones). Not `TaskOutput` or `TaskStop`: they only read or stop a task whose launch
66
+ * read-only ones). Not `TaskOutput`, `TaskStop` or `TaskList`: they only read, list or stop tasks whose launch
60
67
  * already went through `Bash`'s policy (background Bash spec §4.5).
61
68
  */
62
69
  export const SANDBOX_ASK_TOOL_NAMES = new Set([
@@ -158,7 +165,7 @@ export const CHILD_REMOVED_TOOLS = [
158
165
  /**
159
166
  * The builtins every agent has unless it turns them off (tools default-on spec §4), in this
160
167
  * order: {@link BUILTIN_TOOL_NAMES} without the ones the registry manages by rule (`ToolSearch`,
161
- * `Skill`, `SendMessage`, `SubagentHandback`) and without `TaskOutput` / `TaskStop`, which come
168
+ * `Skill`, `SendMessage`, `SubagentHandback`) and without `TaskOutput` / `TaskStop` / `TaskList`, which come
162
169
  * with `Bash` ({@link BASH_COMPANION_TOOLS}). These are also the builtins an agent can turn off.
163
170
  */
164
171
  export const AGENT_BUILTIN_NAMES = BUILTIN_TOOL_NAMES.filter((n) => n !== BUILTIN.ToolSearch &&
@@ -177,6 +184,7 @@ export const BUILTIN_TOOL_KINDS = {
177
184
  [BUILTIN.Deliver]: "tool",
178
185
  [BUILTIN.TaskOutput]: "read",
179
186
  [BUILTIN.TaskStop]: "command",
187
+ [BUILTIN.TaskList]: "read",
180
188
  [BUILTIN.ToolSearch]: "meta",
181
189
  [BUILTIN.Skill]: "meta",
182
190
  [BUILTIN.ReadToolOutput]: "meta",
@@ -14,7 +14,6 @@ import { AgentDocs } from "../../platform/agent-docs.js";
14
14
  import { PlatformOperations } from "../../platform/registry.js";
15
15
  import type { HookRunner } from "../../runtime/hook-calls.js";
16
16
  import { StepLog } from "../../runtime/step-log.js";
17
- import { UsageReporter } from "../../runtime/usage-report.js";
18
17
  import { SandboxService } from "../../sandbox/sandbox.service.js";
19
18
  import { ScheduledTaskStore } from "../../schedules/store.js";
20
19
  import { StorageService } from "../../storage/storage.service.js";
@@ -34,7 +33,7 @@ export declare class BuiltinTools {
34
33
  /** The WebFetch extraction call: the `web_fetch` purpose, its hooks, usage and denials. */
35
34
  purposes: ModelPurposesService, host: {
36
35
  readonly hooks?: HookRunner;
37
- } | undefined, usageReport: UsageReporter, stepLog: StepLog, log: Logger, background: BackgroundTaskStore, finisher: BackgroundTaskFinisher, stopper: BackgroundTaskStopper, platformOps: PlatformOperations, platformActor: PlatformActor, agentDocs: AgentDocs,
36
+ } | undefined, stepLog: StepLog, log: Logger, background: BackgroundTaskStore, finisher: BackgroundTaskFinisher, stopper: BackgroundTaskStopper, platformOps: PlatformOperations, platformActor: PlatformActor, agentDocs: AgentDocs,
38
37
  /** The schedule tools read tasks and grants; their writes go through the task service. */
39
38
  scheduleStore: ScheduledTaskStore,
40
39
  /** `ScheduleUpdate`'s write: its field change and grant changes in one transaction. */
@@ -11,8 +11,10 @@ import { Inject, Injectable, Optional } from "@nestjs/common";
11
11
  import { BackgroundTaskFinisher } from "../../background/finish.js";
12
12
  import { BackgroundTaskStopper } from "../../background/stop.js";
13
13
  import { BackgroundTaskStore } from "../../background/store.js";
14
+ import { readChildAgents } from "../../conversations/agent-tree.js";
14
15
  import { AttachmentsService } from "../../conversations/attachments.service.js";
15
16
  import { EventLog } from "../../conversations/event-log.js";
17
+ import { conversationAncestors } from "../../conversations/tree.js";
16
18
  import { ConversationDevices } from "../../devices/conversation-devices.js";
17
19
  import { ExtensionHost } from "../../extensions/host.js";
18
20
  import { ExtensionPointDefaults } from "../../extensions/point-defaults.js";
@@ -26,7 +28,6 @@ import { AgentDocs } from "../../platform/agent-docs.js";
26
28
  import { PlatformOperations } from "../../platform/registry.js";
27
29
  import { conversationTimeZone, RUN_CONTEXT_CLOCK } from "../../runtime/run-context.service.js";
28
30
  import { StepLog } from "../../runtime/step-log.js";
29
- import { UsageReporter } from "../../runtime/usage-report.js";
30
31
  import { SandboxService } from "../../sandbox/sandbox.service.js";
31
32
  import { ScheduledTaskStore } from "../../schedules/store.js";
32
33
  import { StorageService } from "../../storage/storage.service.js";
@@ -53,7 +54,7 @@ let BuiltinTools = class BuiltinTools {
53
54
  tools;
54
55
  constructor(db, config, outputs, storage, attachments, eventLog, sandbox, devices, points, webPages,
55
56
  /** The WebFetch extraction call: the `web_fetch` purpose, its hooks, usage and denials. */
56
- purposes, host, usageReport, stepLog, log, background, finisher, stopper, platformOps, platformActor, agentDocs,
57
+ purposes, host, stepLog, log, background, finisher, stopper, platformOps, platformActor, agentDocs,
57
58
  /** The schedule tools read tasks and grants; their writes go through the task service. */
58
59
  scheduleStore,
59
60
  /** `ScheduleUpdate`'s write: its field change and grant changes in one transaction. */
@@ -67,6 +68,10 @@ let BuiltinTools = class BuiltinTools {
67
68
  devices,
68
69
  background,
69
70
  tasks: { finisher, stopper },
71
+ subagents: {
72
+ inTree: async (root, id) => id !== root && (await conversationAncestors(db.db, id)).includes(root),
73
+ childrenOf: (orgId, conversationId) => readChildAgents(db.db, orgId, conversationId),
74
+ },
70
75
  attachments,
71
76
  storage,
72
77
  eventLog,
@@ -103,7 +108,7 @@ let BuiltinTools = class BuiltinTools {
103
108
  points,
104
109
  config,
105
110
  cache: webPages,
106
- extract: (ctx, page, prompt) => extractAnswer({ purposes, eventLog, stepLog, config, log, hooks: host?.hooks, usageReport }, ctx, page, prompt),
111
+ extract: (ctx, page, prompt) => extractAnswer({ purposes, eventLog, stepLog, config, log, hooks: host?.hooks }, ctx, page, prompt),
107
112
  }),
108
113
  // the platform's own operations and docs, as the run's person (Platform tool spec)
109
114
  platformTool({
@@ -149,19 +154,18 @@ BuiltinTools = __decorate([
149
154
  __param(9, Inject(WebPages)),
150
155
  __param(10, Inject(ModelPurposesService)),
151
156
  __param(11, Inject(ExtensionHost)),
152
- __param(12, Inject(UsageReporter)),
153
- __param(13, Inject(StepLog)),
154
- __param(14, Inject(KERNEL_LOGGER)),
155
- __param(15, Inject(BackgroundTaskStore)),
156
- __param(16, Inject(BackgroundTaskFinisher)),
157
- __param(17, Inject(BackgroundTaskStopper)),
158
- __param(18, Inject(PlatformOperations)),
159
- __param(19, Inject(PlatformActor)),
160
- __param(20, Inject(AgentDocs)),
161
- __param(21, Inject(ScheduledTaskStore)),
162
- __param(22, Inject(ScheduleUpdates)),
163
- __param(23, Inject(ScheduleToolsPort)),
164
- __param(24, Optional()),
165
- __param(24, Inject(RUN_CONTEXT_CLOCK))
157
+ __param(12, Inject(StepLog)),
158
+ __param(13, Inject(KERNEL_LOGGER)),
159
+ __param(14, Inject(BackgroundTaskStore)),
160
+ __param(15, Inject(BackgroundTaskFinisher)),
161
+ __param(16, Inject(BackgroundTaskStopper)),
162
+ __param(17, Inject(PlatformOperations)),
163
+ __param(18, Inject(PlatformActor)),
164
+ __param(19, Inject(AgentDocs)),
165
+ __param(20, Inject(ScheduledTaskStore)),
166
+ __param(21, Inject(ScheduleUpdates)),
167
+ __param(22, Inject(ScheduleToolsPort)),
168
+ __param(23, Optional()),
169
+ __param(23, Inject(RUN_CONTEXT_CLOCK))
166
170
  ], BuiltinTools);
167
171
  export { BuiltinTools };
@@ -29,19 +29,25 @@ const SPEC = {
29
29
  "Deliver. Other sub-agents of this conversation may run commands in this sandbox at " +
30
30
  "the same time. Always pass description: a few words saying what the command is for; the " +
31
31
  "user sees it instead of the command.\n\n" +
32
+ "Processes a normal call leaves running (started with '&' or nohup) may be stopped when the " +
33
+ "call ends, depending on the device, so use run_in_background for a dev server, a watcher or " +
34
+ "anything that must keep running after the call.\n\n" +
32
35
  "You can use the run_in_background parameter to run the command in the background, which " +
33
36
  "allows you to continue working while the command runs, e.g. a dev server, a long build or a " +
34
37
  "test suite. The call returns at once with a task id and the command's output goes to a file " +
35
38
  "under .capybara/tasks in the workspace directory. Read the output with TaskOutput (or Read) and stop the " +
36
- "task with TaskStop; you will be told when it finishes. You do not need to use '&' at the end " +
39
+ "task with TaskStop; you will be told when it finishes. TaskList lists this conversation's " +
40
+ "background tasks and their ids, running ones first. You do not need to use '&' at the end " +
37
41
  "of the command when using this parameter. In the background, timeout defaults to the " +
38
42
  "deployment's background maximum (7200000 by default). A background task ends when its " +
39
43
  "command exits, and processes it left running are stopped. A background task is lost if the " +
40
44
  "sandbox is suspended (it is suspended after it has been running for a while, " +
41
45
  "sandbox.limits.maxRunningMinutes), so keep its results in files under the workspace directory.\n\n" +
42
46
  "Network access goes through a proxy set in HTTP_PROXY / HTTPS_PROXY / ALL_PROXY; a tool " +
43
- "that ignores them fails with 'Operation not permitted'. A blocked domain is reported after " +
44
- "the output.",
47
+ "that ignores them fails with 'Operation not permitted' or cannot resolve names. Such tools, " +
48
+ "browsers for example, need the proxy passed explicitly: for Chromium (also headless, as " +
49
+ "Playwright or Puppeteer launch it) pass --proxy-server=$HTTPS_PROXY. A blocked domain is " +
50
+ "reported after the output.",
45
51
  inputSchema: {
46
52
  type: "object",
47
53
  properties: {
@@ -3,6 +3,7 @@ import type { BackgroundTaskFinisher } from "../../../background/finish.js";
3
3
  import type { BackgroundTaskStopper } from "../../../background/stop.js";
4
4
  import type { BackgroundTaskStore } from "../../../background/store.js";
5
5
  import type { KernelConfig } from "../../../config/schema.js";
6
+ import type { ChildAgent } from "../../../conversations/agent-tree.js";
6
7
  import type { AttachmentsService } from "../../../conversations/attachments.service.js";
7
8
  import type { EventLog } from "../../../conversations/event-log.js";
8
9
  import { type ConversationDevices } from "../../../devices/conversation-devices.js";
@@ -25,7 +26,16 @@ export interface SandboxToolDeps {
25
26
  */
26
27
  devices: Pick<ConversationDevices, "forTool"> & Partial<Pick<ConversationDevices, "usableNow" | "sandboxLevel">>;
27
28
  /** Background `Bash` tasks; absent in test stubs, where `run_in_background` is refused. */
28
- background?: Pick<BackgroundTaskStore, "reserve" | "markStarted" | "failLaunch" | "finishStoppedLaunch" | "releaseStop" | "get" | "advanceOffset">;
29
+ background?: Pick<BackgroundTaskStore, "reserve" | "markStarted" | "failLaunch" | "finishStoppedLaunch" | "releaseStop" | "get" | "advanceOffset" | "runningOwnedBy" | "finishedOwnedBy">;
30
+ /**
31
+ * Whether `id` is a sub-agent conversation in the tree under `rootConversationId` (TaskOutput /
32
+ * TaskStop tell a sub-agent's agentId apart from an unknown task id), and the sub-agents a
33
+ * conversation dispatched itself (`TaskList`); absent in test stubs.
34
+ */
35
+ subagents?: {
36
+ inTree(rootConversationId: string, id: string): Promise<boolean>;
37
+ childrenOf(orgId: string, conversationId: string): Promise<ChildAgent[]>;
38
+ };
29
39
  /**
30
40
  * What `TaskOutput` and `TaskStop` finish and stop tasks with; absent in test stubs, where both
31
41
  * answer that background tasks are not available.
@@ -83,6 +93,13 @@ export declare function noSandboxError(): ToolError;
83
93
  export declare const SANDBOX_UNREACHABLE_TEXT = "The sandbox provider can't be reached (the connection failed), so this call did nothing in the sandbox; retrying won't help until a super admin checks the sandbox extension's configuration (Admin console \u2192 Extensions). Continue without the sandbox, or ask the person to have a super admin check it.";
84
94
  /** A provider connection that broke during the call: the provider may have acted before it broke. */
85
95
  export declare const SANDBOX_CONNECTION_DROPPED_TEXT = "The connection to the sandbox provider broke during this call, so it may or may not have taken effect; check the sandbox state before retrying. If it fails again, continue without the sandbox and ask the person to have a super admin check the sandbox extension's configuration (Admin console \u2192 Extensions).";
96
+ /**
97
+ * A cloud provider refused the device's tier (`sandbox.tier_refused`, spec §5.3): the tier the
98
+ * device asks for, never the provider's own message (it may name its settings).
99
+ */
100
+ export declare const sandboxTierRefusedText: (tier: string) => string;
101
+ /** A cloud provider failed to create the device's sandbox otherwise (`sandbox.create_failed`). */
102
+ export declare const SANDBOX_CREATE_FAILED_TEXT = "The cloud sandbox provider could not create this device's sandbox, so nothing ran in it. Retry once; if it fails again, continue without the sandbox and ask the person to have a super admin check the sandbox extension's settings (Admin console \u2192 Extensions).";
86
103
  /** The directory of attachments in the device (each conversation has a subdirectory). */
87
104
  export declare const attachmentsRoot: (roots: SandboxRoots) => string;
88
105
  /** {@link attachmentsRoot} of an instance with the default roots. */
@@ -1,4 +1,4 @@
1
- import { DEFAULT_SANDBOX_ROOTS, DeviceProtocolError, isFileSystemGoneError, isSandboxGoneError, ToolError, validateSandboxPath, validateSandboxPathShape, } from "@capybara-hq/plugin-sdk";
1
+ import { DEFAULT_SANDBOX_ROOTS, DeviceProtocolError, isFileSystemGoneError, isSandboxGoneError, isSandboxTier, ToolError, validateSandboxPath, validateSandboxPathShape, } from "@capybara-hq/plugin-sdk";
2
2
  import { ANONYMOUS_NO_DEVICE, } from "../../../devices/conversation-devices.js";
3
3
  import { ApiError } from "../../../errors.js";
4
4
  import { UNSANDBOXED_NOT_ALLOWED_ERROR } from "../../../local-devices/local-device-provider.js";
@@ -48,6 +48,13 @@ const DEVICE_OFFLINE_TEXT = "The device's machine is offline. Ask the person to
48
48
  const DEVICE_OUTDATED_TEXT = "The device's machine runs an outdated capybara-device. Ask the person to update it on that machine and start it again.";
49
49
  const DEVICE_REVOKED_TEXT = "The device's machine was disconnected. Ask the person to run capybara-device logout, then capybara-device login on it, or to start a new conversation on another device.";
50
50
  const deviceProtocolText = (detail) => `The device's machine sent an answer the platform cannot read (${detail}). Ask the person to update capybara-device on that machine, then retry.`;
51
+ /**
52
+ * A cloud provider refused the device's tier (`sandbox.tier_refused`, spec §5.3): the tier the
53
+ * device asks for, never the provider's own message (it may name its settings).
54
+ */
55
+ export const sandboxTierRefusedText = (tier) => `The cloud sandbox can't run this device at the ${tier} tier: the sandbox provider refused to create it there, so nothing ran in the sandbox and retrying won't help. Ask the person to lower the device's sandbox tier on its device page, or to have an org admin lower the org's highest tier in the sandbox settings, or a super admin to allow that tier in the sandbox extension's settings (Admin console → Extensions). Continue without the sandbox meanwhile.`;
56
+ /** A cloud provider failed to create the device's sandbox otherwise (`sandbox.create_failed`). */
57
+ export const SANDBOX_CREATE_FAILED_TEXT = "The cloud sandbox provider could not create this device's sandbox, so nothing ran in it. Retry once; if it fails again, continue without the sandbox and ask the person to have a super admin check the sandbox extension's settings (Admin console → Extensions).";
51
58
  const LOCK_LOST_TEXT = "The sandbox operation was interrupted because the sandbox lock was lost; check the sandbox state and retry.";
52
59
  /** The directory of attachments in the device (each conversation has a subdirectory). */
53
60
  export const attachmentsRoot = (roots) => `${roots.workspace}/attachments`;
@@ -357,6 +364,12 @@ export function mapSandboxError(e, log) {
357
364
  return new ToolError(UNSANDBOXED_TEXT);
358
365
  if (e.code === "device.tier_not_granted")
359
366
  return new ToolError(UNATTENDED_OPEN_TIER_ERROR);
367
+ if (e.code === "sandbox.tier_refused") {
368
+ const tier = detail?.tier;
369
+ return new ToolError(sandboxTierRefusedText(isSandboxTier(tier) ? tier : "requested"));
370
+ }
371
+ if (e.code === "sandbox.create_failed")
372
+ return new ToolError(SANDBOX_CREATE_FAILED_TEXT);
360
373
  }
361
374
  // an operation outside withSandbox's own mapping (it records lost files itself)
362
375
  if (isFileSystemGoneError(e))
@@ -11,6 +11,7 @@ export { compileGlob, GlobPatternError, MAX_GLOB_STARS } from "./glob-match.js";
11
11
  * a provider for it. Allowed by default; with the org setting `sandboxToolPolicy` set to ask,
12
12
  * every one but Read / Glob / Grep needs approval (see policy.ts).
13
13
  * `TaskOutput` and `TaskStop` read and stop background `Bash` tasks through
14
- * `SandboxService.ifRunning` (never creating or resuming a sandbox); always allowed.
14
+ * `SandboxService.ifRunning` (never creating or resuming a sandbox); `TaskList` lists them and the
15
+ * conversation's sub-agents from PG; all three always allowed.
15
16
  */
16
17
  export declare function sandboxTools(deps: SandboxToolDeps): BuiltinTool[];
@@ -4,6 +4,7 @@ import { sandboxEditTool } from "./edit.js";
4
4
  import { sandboxGlobTool } from "./glob.js";
5
5
  import { sandboxGrepTool } from "./grep.js";
6
6
  import { sandboxReadTool } from "./read.js";
7
+ import { taskListTool } from "./task-list.js";
7
8
  import { taskOutputTool } from "./task-output.js";
8
9
  import { taskStopTool } from "./task-stop.js";
9
10
  import { sandboxWriteTool } from "./write.js";
@@ -18,7 +19,8 @@ export { compileGlob, GlobPatternError, MAX_GLOB_STARS } from "./glob-match.js";
18
19
  * a provider for it. Allowed by default; with the org setting `sandboxToolPolicy` set to ask,
19
20
  * every one but Read / Glob / Grep needs approval (see policy.ts).
20
21
  * `TaskOutput` and `TaskStop` read and stop background `Bash` tasks through
21
- * `SandboxService.ifRunning` (never creating or resuming a sandbox); always allowed.
22
+ * `SandboxService.ifRunning` (never creating or resuming a sandbox); `TaskList` lists them and the
23
+ * conversation's sub-agents from PG; all three always allowed.
22
24
  */
23
25
  export function sandboxTools(deps) {
24
26
  return [
@@ -31,5 +33,6 @@ export function sandboxTools(deps) {
31
33
  sandboxDeliverTool(deps),
32
34
  taskOutputTool(deps),
33
35
  taskStopTool(deps),
36
+ taskListTool(deps),
34
37
  ];
35
38
  }
@@ -0,0 +1,12 @@
1
+ import type { BuiltinTool } from "../../registry.js";
2
+ import { type SandboxToolDeps } from "./common.js";
3
+ /** How many finished background tasks (and finished sub-agents) `TaskList` lists at most. */
4
+ export declare const TASK_LIST_FINISHED_LIMIT = 20;
5
+ /**
6
+ * Built-in `TaskList`: the calling conversation's own background tasks (never its sub-agents' or
7
+ * another conversation's: owner `conversation_id`, as `TaskOutput` checks) and the sub-agents it
8
+ * dispatched itself. Running entries first (oldest first), then with `status: "all"` the
9
+ * {@link TASK_LIST_FINISHED_LIMIT} most recently finished. Reads PG only: it never checks a task in
10
+ * its sandbox, so a status is the poller's or a tool's last one.
11
+ */
12
+ export declare function taskListTool(deps: SandboxToolDeps): BuiltinTool;