@capybara-hq/kernel 0.1.0-rc.11 → 0.1.0-rc.13

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 (220) hide show
  1. package/dist/admin/admin.schemas.d.ts +13 -0
  2. package/dist/agents/agent-lookup.d.ts +5 -0
  3. package/dist/agents/agent-lookup.js +15 -0
  4. package/dist/agents/agents.schemas.d.ts +3 -0
  5. package/dist/agents/agents.service.js +11 -1
  6. package/dist/agents/definition.d.ts +1 -0
  7. package/dist/agents/definition.js +7 -3
  8. package/dist/agents/org-templates.js +4 -0
  9. package/dist/agents/pending-connectors.service.js +1 -1
  10. package/dist/background/background.module.js +2 -2
  11. package/dist/background/launch-lock.d.ts +1 -1
  12. package/dist/background/launch-lock.js +1 -1
  13. package/dist/background/poll.job.d.ts +2 -2
  14. package/dist/background/poll.job.js +10 -10
  15. package/dist/background/stop.d.ts +1 -1
  16. package/dist/background/stop.js +3 -3
  17. package/dist/background/store.d.ts +5 -5
  18. package/dist/background/store.js +5 -5
  19. package/dist/background/tasks.service.d.ts +6 -6
  20. package/dist/background/tasks.service.js +13 -13
  21. package/dist/catalog/component-scope.controller.d.ts +18 -6
  22. package/dist/catalog/component-scope.controller.js +36 -13
  23. package/dist/catalog/component-scope.service.d.ts +11 -0
  24. package/dist/catalog/component-scope.service.js +4 -0
  25. package/dist/catalog/component-state.d.ts +8 -0
  26. package/dist/catalog/component-state.js +19 -11
  27. package/dist/catalog/marketplaces.service.d.ts +1 -1
  28. package/dist/catalog/plugin-import.js +4 -1
  29. package/dist/channels/channels.module.js +2 -2
  30. package/dist/channels/inbound.d.ts +8 -8
  31. package/dist/channels/inbound.js +31 -31
  32. package/dist/channels/phrases.d.ts +27 -27
  33. package/dist/channels/phrases.js +27 -27
  34. package/dist/config/docs.js +1 -1
  35. package/dist/config/schema.d.ts +2 -2
  36. package/dist/config/schema.js +21 -21
  37. package/dist/conversations/conversations.controller.d.ts +11 -11
  38. package/dist/conversations/conversations.controller.js +15 -15
  39. package/dist/conversations/conversations.module.d.ts +1 -1
  40. package/dist/conversations/conversations.module.js +7 -7
  41. package/dist/conversations/conversations.schemas.js +1 -1
  42. package/dist/conversations/conversations.service.d.ts +23 -14
  43. package/dist/conversations/conversations.service.js +28 -21
  44. package/dist/conversations/{environment-status.d.ts → device-status.d.ts} +9 -9
  45. package/dist/conversations/{environment-status.js → device-status.js} +18 -18
  46. package/dist/conversations/event-log.d.ts +1 -1
  47. package/dist/conversations/event-log.js +1 -1
  48. package/dist/conversations/events.d.ts +2 -1
  49. package/dist/conversations/events.js +2 -2
  50. package/dist/ctx/create-ctx.d.ts +10 -1
  51. package/dist/ctx/create-ctx.js +86 -25
  52. package/dist/db/kernel-schema.d.ts +39 -23
  53. package/dist/{environments → devices}/access.d.ts +19 -19
  54. package/dist/{environments → devices}/access.js +28 -28
  55. package/dist/devices/conversation-devices.d.ts +75 -0
  56. package/dist/{environments/conversation-environments.js → devices/conversation-devices.js} +71 -72
  57. package/dist/devices/devices.controller.d.ts +36 -0
  58. package/dist/{environments/environments.controller.js → devices/devices.controller.js} +37 -37
  59. package/dist/devices/devices.module.d.ts +5 -13
  60. package/dist/devices/devices.module.js +16 -29
  61. package/dist/devices/devices.schemas.d.ts +3 -0
  62. package/dist/devices/devices.schemas.js +14 -0
  63. package/dist/{environments/environments.service.d.ts → devices/devices.service.d.ts} +70 -70
  64. package/dist/{environments/environments.service.js → devices/devices.service.js} +122 -122
  65. package/dist/devices/dto.d.ts +13 -54
  66. package/dist/devices/dto.js +7 -39
  67. package/dist/devices/label.d.ts +11 -0
  68. package/dist/{environments → devices}/label.js +7 -7
  69. package/dist/{environments → devices}/legacy-cleanup.job.d.ts +3 -3
  70. package/dist/{environments → devices}/legacy-cleanup.job.js +3 -3
  71. package/dist/errors.d.ts +27 -25
  72. package/dist/errors.js +27 -25
  73. package/dist/evals/recorded-tools.d.ts +2 -2
  74. package/dist/evals/recorded-tools.js +6 -6
  75. package/dist/extensions/admin-ops.js +1 -0
  76. package/dist/extensions/admin-view.js +2 -0
  77. package/dist/extensions/effective-config.d.ts +6 -0
  78. package/dist/extensions/effective-config.js +18 -7
  79. package/dist/extensions/host.d.ts +13 -2
  80. package/dist/extensions/host.js +24 -3
  81. package/dist/extensions/instance.d.ts +12 -1
  82. package/dist/extensions/instance.js +13 -0
  83. package/dist/extensions/permission-view.d.ts +10 -1
  84. package/dist/files/previewers.d.ts +1 -1
  85. package/dist/files/previewers.js +1 -1
  86. package/dist/http/api.module.js +10 -6
  87. package/dist/http/gateway-routes.d.ts +1 -1
  88. package/dist/http/gateway-routes.js +1 -1
  89. package/dist/identity/settings.d.ts +32 -0
  90. package/dist/identity/settings.js +26 -2
  91. package/dist/infra/config/kernel-config.service.js +4 -4
  92. package/dist/kernel.module.js +3 -3
  93. package/dist/{devices → local-devices}/device-auth.controller.d.ts +6 -6
  94. package/dist/{devices → local-devices}/device-auth.controller.js +5 -5
  95. package/dist/{devices → local-devices}/device-auth.d.ts +20 -20
  96. package/dist/{devices → local-devices}/device-auth.js +51 -53
  97. package/dist/local-devices/device-auth.module.d.ts +7 -0
  98. package/dist/{devices → local-devices}/device-auth.module.js +5 -5
  99. package/dist/{devices → local-devices}/device-routes.d.ts +3 -3
  100. package/dist/{devices → local-devices}/device-routes.js +2 -2
  101. package/dist/local-devices/dto.d.ts +66 -0
  102. package/dist/local-devices/dto.js +44 -0
  103. package/dist/{devices → local-devices}/keys.d.ts +11 -11
  104. package/dist/{devices → local-devices}/keys.js +11 -11
  105. package/dist/{devices/device-gateway.d.ts → local-devices/local-device-gateway.d.ts} +14 -14
  106. package/dist/{devices/device-gateway.js → local-devices/local-device-gateway.js} +44 -44
  107. package/dist/{devices/device-provider.d.ts → local-devices/local-device-provider.d.ts} +15 -15
  108. package/dist/{devices/device-provider.js → local-devices/local-device-provider.js} +23 -23
  109. package/dist/local-devices/local-devices.module.d.ts +16 -0
  110. package/dist/local-devices/local-devices.module.js +44 -0
  111. package/dist/{devices → local-devices}/machines.d.ts +26 -26
  112. package/dist/{devices → local-devices}/machines.js +71 -71
  113. package/dist/{devices → local-devices}/relay.d.ts +15 -15
  114. package/dist/{devices → local-devices}/relay.js +55 -55
  115. package/dist/{devices → local-devices}/targets.d.ts +8 -8
  116. package/dist/{devices → local-devices}/targets.js +14 -14
  117. package/dist/orgs/offboarding.d.ts +6 -6
  118. package/dist/orgs/offboarding.js +12 -12
  119. package/dist/orgs/settings.service.d.ts +28 -0
  120. package/dist/orgs/settings.service.js +20 -1
  121. package/dist/platform/exclusions.js +48 -3
  122. package/dist/platform/operation.js +6 -2
  123. package/dist/platform/ops/admin.ops.js +8 -2
  124. package/dist/platform/ops/agents.ops.js +19 -6
  125. package/dist/platform/ops/conversations.ops.d.ts +3 -3
  126. package/dist/platform/ops/conversations.ops.js +9 -9
  127. package/dist/platform/ops/devices.ops.d.ts +26 -0
  128. package/dist/platform/ops/devices.ops.js +311 -0
  129. package/dist/platform/ops/org-settings.ops.js +2 -2
  130. package/dist/platform/ops/orgs.ops.js +1 -0
  131. package/dist/platform/ops/ui.ops.d.ts +8 -0
  132. package/dist/platform/ops/ui.ops.js +41 -0
  133. package/dist/platform/platform-ops.module.d.ts +3 -2
  134. package/dist/platform/platform-ops.module.js +8 -5
  135. package/dist/platform/route-schemas.js +6 -3
  136. package/dist/roles/gateway-role.d.ts +3 -3
  137. package/dist/roles/gateway-role.js +4 -4
  138. package/dist/roles/gateway.module.d.ts +1 -1
  139. package/dist/roles/gateway.module.js +5 -5
  140. package/dist/roles/worker.module.js +1 -1
  141. package/dist/runtime/context/build.d.ts +1 -1
  142. package/dist/runtime/context/build.js +6 -6
  143. package/dist/runtime/context/compaction.js +1 -1
  144. package/dist/runtime/context/{environment-paths.d.ts → device-paths.d.ts} +11 -11
  145. package/dist/runtime/context/{environment-paths.js → device-paths.js} +13 -13
  146. package/dist/runtime/maintenance.d.ts +1 -1
  147. package/dist/runtime/subagents/definition.d.ts +3 -2
  148. package/dist/runtime/subagents/definition.js +2 -1
  149. package/dist/sandbox/jobs.d.ts +13 -13
  150. package/dist/sandbox/jobs.js +20 -20
  151. package/dist/sandbox/leases.d.ts +9 -9
  152. package/dist/sandbox/leases.js +14 -14
  153. package/dist/sandbox/providers.d.ts +8 -8
  154. package/dist/sandbox/providers.js +8 -8
  155. package/dist/sandbox/sandbox.module.d.ts +1 -1
  156. package/dist/sandbox/sandbox.module.js +1 -1
  157. package/dist/sandbox/sandbox.service.d.ts +76 -76
  158. package/dist/sandbox/sandbox.service.js +230 -230
  159. package/dist/tools/builtins/builtin-tools.d.ts +2 -2
  160. package/dist/tools/builtins/builtin-tools.js +5 -5
  161. package/dist/tools/builtins/platform.js +1 -1
  162. package/dist/tools/builtins/sandbox/bash.js +6 -6
  163. package/dist/tools/builtins/sandbox/common.d.ts +25 -25
  164. package/dist/tools/builtins/sandbox/common.js +51 -51
  165. package/dist/tools/builtins/sandbox/edit.js +2 -2
  166. package/dist/tools/builtins/sandbox/index.d.ts +1 -1
  167. package/dist/tools/builtins/sandbox/index.js +1 -1
  168. package/dist/tools/builtins/sandbox/task-output.d.ts +6 -6
  169. package/dist/tools/builtins/sandbox/task-output.js +10 -10
  170. package/dist/tools/builtins/sandbox/task-stop.js +2 -2
  171. package/dist/tools/builtins/sandbox/write.js +2 -2
  172. package/dist/tools/builtins/skill-load.d.ts +1 -1
  173. package/dist/tools/builtins/skill-load.js +2 -2
  174. package/dist/tools/connector-account-policies.d.ts +51 -0
  175. package/dist/tools/connector-account-policies.js +165 -0
  176. package/dist/tools/connectors.controller.d.ts +35 -4
  177. package/dist/tools/connectors.controller.js +66 -7
  178. package/dist/tools/connectors.schemas.d.ts +11 -0
  179. package/dist/tools/connectors.schemas.js +11 -0
  180. package/dist/tools/connectors.service.d.ts +2 -0
  181. package/dist/tools/connectors.service.js +1 -0
  182. package/dist/tools/pipeline.d.ts +1 -1
  183. package/dist/tools/pipeline.js +4 -3
  184. package/dist/tools/policy.d.ts +23 -1
  185. package/dist/tools/policy.js +99 -24
  186. package/dist/tools/registry.d.ts +2 -2
  187. package/dist/tools/tools.module.js +6 -3
  188. package/dist/ui/admin-ui.controller.d.ts +56 -0
  189. package/dist/ui/admin-ui.controller.js +192 -0
  190. package/dist/ui/arrangement.d.ts +74 -0
  191. package/dist/ui/arrangement.js +98 -0
  192. package/dist/ui/embed-token.d.ts +2 -2
  193. package/dist/ui/embed-token.js +30 -5
  194. package/dist/ui/registry.d.ts +29 -14
  195. package/dist/ui/registry.js +31 -10
  196. package/dist/ui/slots.controller.d.ts +11 -0
  197. package/dist/ui/slots.controller.js +25 -0
  198. package/dist/ui/ui-stream.d.ts +20 -3
  199. package/dist/ui/ui-stream.js +25 -7
  200. package/dist/ui/ui.schemas.d.ts +66 -0
  201. package/dist/ui/ui.schemas.js +65 -0
  202. package/dist/ui/views.controller.d.ts +35 -49
  203. package/dist/ui/views.controller.js +48 -70
  204. package/dist/ui/views.d.ts +84 -18
  205. package/dist/ui/views.js +201 -38
  206. package/package.json +3 -3
  207. package/src/migrations/0104_connector_tool_account_policies.sql +16 -0
  208. package/src/migrations/0105_devices_rename.sql +136 -0
  209. package/dist/devices/device-auth.module.d.ts +0 -7
  210. package/dist/environments/conversation-environments.d.ts +0 -75
  211. package/dist/environments/dto.d.ts +0 -25
  212. package/dist/environments/dto.js +0 -12
  213. package/dist/environments/environments.controller.d.ts +0 -36
  214. package/dist/environments/environments.module.d.ts +0 -8
  215. package/dist/environments/environments.module.js +0 -31
  216. package/dist/environments/environments.schemas.d.ts +0 -3
  217. package/dist/environments/environments.schemas.js +0 -14
  218. package/dist/environments/label.d.ts +0 -11
  219. package/dist/platform/ops/environments.ops.d.ts +0 -26
  220. package/dist/platform/ops/environments.ops.js +0 -311
@@ -2,6 +2,7 @@ import { sql } from "kysely";
2
2
  import { roleAtLeast } from "../orgs/roles.js";
3
3
  import { readOrgSettings } from "../orgs/settings.service.js";
4
4
  import { R3_FIXED_ASK_TOOL_KEYS, R3_SHARED_AGENT_ONLY_TOOL_KEYS, SANDBOX_ASK_TOOL_NAMES, } from "./builtin-names.js";
5
+ import { personalAuth } from "./connector-account-policies.js";
5
6
  import { normalizeStoredDefinitionIn } from "./legacy-data.js";
6
7
  export { R3_FIXED_ASK_TOOL_KEYS, R3_SHARED_AGENT_ONLY_TOOL_KEYS } from "./builtin-names.js";
7
8
  const RANK = { allow: 0, ask: 1, deny: 2 };
@@ -35,17 +36,51 @@ export function isFixedAsk(tool, visibility, skillWritePolicy) {
35
36
  // 私有 agent:缺省 / allow 不问(R4),负责人设为 ask(D10)时与共享 agent 一样固定询问
36
37
  return !(builtinOnly && visibility === "private" && skillWritePolicy !== "ask");
37
38
  }
38
- const asOverride = (v) => v === "ask" || v === "deny" ? v : undefined;
39
+ const asSetting = (v) => v === "allow" || v === "ask" || v === "deny" ? v : undefined;
40
+ /** The stricter of the settings that are set (none set: undefined). */
41
+ function combine(...xs) {
42
+ let out;
43
+ for (const x of xs)
44
+ if (x !== undefined)
45
+ out = out === undefined ? x : stricter(out, x);
46
+ return out;
47
+ }
39
48
  /**
40
- * An agent's tightening for a call: its policy on the tool key and, for a per-call key
41
- * (`Platform:<operation>`), on that key, the stricter of the two (a policy can only tighten).
49
+ * An agent's setting for a call: its policy on the tool key and, for a per-call key
50
+ * (`Platform:<operation>`), on that key, the stricter of the two; undefined when neither is set.
51
+ * `allow` counts only for a connector tool ({@link connectorPolicy}); elsewhere an agent's policy
52
+ * can only tighten ({@link tighteningOf}).
42
53
  */
43
- function overrideOf(policies, toolKey, policyKey) {
44
- const own = asOverride(policies?.[toolKey]);
54
+ function settingOf(policies, toolKey, policyKey) {
55
+ const own = asSetting(policies?.[toolKey]);
45
56
  if (policyKey === toolKey)
46
57
  return own;
47
- const s = stricter(own ?? "allow", asOverride(policies?.[policyKey]));
48
- return s === "allow" ? undefined : s;
58
+ return combine(own, asSetting(policies?.[policyKey]));
59
+ }
60
+ /** An agent setting as a tightening (`allow` tightens nothing). */
61
+ const tighteningOf = (s) => s === "ask" || s === "deny" ? s : undefined;
62
+ /**
63
+ * A personal-credential connector tool's policy for one call (maintainer decisions 2026-11-09;
64
+ * an org-credential connector's tool keeps the org default with the agent's tightening only),
65
+ * highest first:
66
+ * 1. the org's default `deny` (a tool the org switched off never gets here: it is unavailable);
67
+ * 2. the member's own `deny` for calls with their connection (`member`): a floor no agent lifts;
68
+ * 3. the agent's setting (`agent`, from its tools page): `allow` / `ask` / `deny`;
69
+ * 4. the member's own `allow` / `ask`;
70
+ * 5. the org's default (`connector_tools.default_policy`, the annotation default unless an org
71
+ * admin changed it: `readOnlyHint: true` allows, anything else asks).
72
+ * Fixed asks (R3, `requiresUserInteraction`), role escalation and grants apply after this.
73
+ */
74
+ export function connectorPolicy(input) {
75
+ if (input.orgDefault === "deny")
76
+ return { policy: "deny", source: "org" };
77
+ if (input.member === "deny")
78
+ return { policy: "deny", source: "member" };
79
+ if (input.agent !== undefined)
80
+ return { policy: input.agent, source: "agent" };
81
+ if (input.member !== undefined)
82
+ return { policy: input.member, source: "member" };
83
+ return { policy: input.orgDefault, source: "org" };
49
84
  }
50
85
  /**
51
86
  * 执行时的策略判定(spec 3.6 R5:在动作执行那一刻判定)。每次调用都实时读取:
@@ -58,7 +93,7 @@ function overrideOf(policies, toolKey, policyKey) {
58
93
  * parent's current version still tightens it (sub-agents spec §5.3, §9); its "this conversation"
59
94
  * grants are the root conversation's (agent map spec §7); an org agent sub-agent (§5.5): `ctx.agentId` is the
60
95
  * dispatched agent (its current version, its "always" grants) and the parent agent's current
61
- * policies apply on top ({@link parentAgentOverride});
96
+ * policies apply on top ({@link parentAgentSetting});
62
97
  * - 授权表(`conversation_tool_grants` / `tool_grants`),`spec_hash` 与当前定义不一致视为无效(D8),
63
98
  * `connector_id` 与工具的来源连接器不一致也无效(删除后用同一 slug 重新安装的连接器不继承旧授权);
64
99
  * - 工具声明了 `requiredRole`(R1)时,发起人在组织中的当前角色:不是成员 → 拒绝;角色不足 → 按角色升级的审批
@@ -75,11 +110,15 @@ export function createDecidePolicy(db) {
75
110
  let connectorDefault = call?.policy ?? tool.defaultPolicy;
76
111
  let connectorId = null;
77
112
  let hashCurrent = true;
113
+ /** The member's own setting for a call with their connection (a personal-credential connector). */
114
+ let memberSetting;
115
+ /** A personal-credential connector: calls use the acting account's own connection. */
116
+ let personal = false;
78
117
  if (tool.source.kind === "connector") {
79
118
  connectorId = tool.source.connectorId;
80
119
  const app = await db
81
120
  .selectFrom("connectors")
82
- .select(["slug", "status", "deleted_at"])
121
+ .select(["slug", "status", "deleted_at", "auth"])
83
122
  .where("id", "=", tool.source.connectorId)
84
123
  .where("org_id", "=", ctx.orgId)
85
124
  .executeTakeFirst();
@@ -105,6 +144,19 @@ export function createDecidePolicy(db) {
105
144
  return { unavailable: "Tool is pending review" };
106
145
  connectorDefault = row.default_policy;
107
146
  hashCurrent = row.spec_hash === tool.specHash;
147
+ // The call is made with the acting account's own connection (the credential the pipeline
148
+ // injects for `ctx.accountId`): that account's settings apply, never another's
149
+ personal = personalAuth(app.auth);
150
+ if (personal) {
151
+ const mine = await db
152
+ .selectFrom("connector_tool_account_policies")
153
+ .select("policy")
154
+ .where("connector_id", "=", tool.source.connectorId)
155
+ .where("account_id", "=", ctx.accountId)
156
+ .where("tool_name", "=", tool.source.name)
157
+ .executeTakeFirst();
158
+ memberSetting = mine?.policy;
159
+ }
108
160
  }
109
161
  // 沙箱工具的组织默认(M5 T8):组织设置 sandboxToolPolicy 为 ask 时,有副作用的沙箱工具至少 ask
110
162
  // `orgAsk`: the org setting, not the tool's own default, is what makes it ask
@@ -116,7 +168,7 @@ export function createDecidePolicy(db) {
116
168
  }
117
169
  }
118
170
  // 运行所用版本可能是加 toolPolicies 字段之前保存的:缺省为空
119
- const runOverride = overrideOf(agentDef.toolPolicies, tool.key, policyKey);
171
+ const runSetting = settingOf(agentDef.toolPolicies, tool.key, policyKey);
120
172
  const current = await db
121
173
  .selectFrom("agents")
122
174
  .innerJoin("agent_versions", (j) => j
@@ -130,12 +182,33 @@ export function createDecidePolicy(db) {
130
182
  ? undefined
131
183
  : await normalizeStoredDefinitionIn(db, ctx.orgId, current.definition);
132
184
  const currentPolicies = currentDef?.toolPolicies;
133
- const currentOverride = overrideOf(currentPolicies, tool.key, policyKey);
134
- let strictest = stricter(runOverride ?? "allow", currentOverride);
185
+ const currentSetting = settingOf(currentPolicies, tool.key, policyKey);
186
+ // the run's version, the agent's current version and (a sub-agent) its parent agent's
187
+ // current version: the stricter of the ones that are set
188
+ // (an `allow` the owner removed since the run started no longer applies: removing it tightens)
189
+ const runKept = runSetting === "allow" && current !== undefined && currentSetting === undefined
190
+ ? undefined
191
+ : runSetting;
192
+ let agentSetting = combine(runKept, currentSetting);
135
193
  if (ctx.parentConversationId !== undefined && ctx.parentAgentDiffers !== false) {
136
- strictest = stricter(strictest, await parentAgentOverride(db, tool.key, policyKey, ctx));
194
+ agentSetting = combine(agentSetting, await parentAgentSetting(db, tool.key, policyKey, ctx));
195
+ }
196
+ let agentOverride = tighteningOf(agentSetting);
197
+ // A personal-credential connector's tool: the levels of `connectorPolicy` decide its base
198
+ // policy (an agent's `allow` counts here, below the org's and the member's deny). A connector
199
+ // with one org credential has no member level and its calls act as the whole org: there the
200
+ // agent's setting only tightens the org default, as for every other tool (review 2026-11-09)
201
+ let decidedBy = null;
202
+ if (tool.source.kind === "connector" && personal) {
203
+ const r = connectorPolicy({
204
+ orgDefault: connectorDefault,
205
+ member: memberSetting,
206
+ agent: agentSetting,
207
+ });
208
+ connectorDefault = r.policy;
209
+ decidedBy = r.source;
210
+ agentOverride = undefined;
137
211
  }
138
- const agentOverride = strictest === "allow" ? undefined : strictest;
139
212
  let escalateTo = null;
140
213
  if (tool.requiredRole !== undefined) {
141
214
  const m = await db
@@ -184,15 +257,17 @@ export function createDecidePolicy(db) {
184
257
  // default
185
258
  policy: escalateTo !== null
186
259
  ? "role"
187
- : agentOverride === "ask"
260
+ : agentOverride === "ask" || (decidedBy === "agent" && connectorDefault === "ask")
188
261
  ? "agent"
189
- : tool.source.kind === "connector" && connectorDefault === "ask"
190
- ? "connector"
191
- : fixedAsk
192
- ? "fixed"
193
- : orgAsk
194
- ? "org"
195
- : "default",
262
+ : decidedBy === "member" && connectorDefault === "ask"
263
+ ? "member"
264
+ : tool.source.kind === "connector" && connectorDefault === "ask"
265
+ ? "connector"
266
+ : fixedAsk
267
+ ? "fixed"
268
+ : orgAsk
269
+ ? "org"
270
+ : "default",
196
271
  };
197
272
  return { ask };
198
273
  };
@@ -203,7 +278,7 @@ export function createDecidePolicy(db) {
203
278
  * policy for the same key applies on top, so the parent's owner can tighten a running child too.
204
279
  * For a template or general-purpose child the parent conversation has the same agent, already read.
205
280
  */
206
- async function parentAgentOverride(db, toolKey, policyKey, ctx) {
281
+ async function parentAgentSetting(db, toolKey, policyKey, ctx) {
207
282
  const row = await db
208
283
  .selectFrom("conversations as c")
209
284
  .innerJoin("agents as a", "a.id", "c.agent_id")
@@ -216,7 +291,7 @@ async function parentAgentOverride(db, toolKey, policyKey, ctx) {
216
291
  // stored raw: an rc version's keys read as current keys, so its deny keeps applying (§8.3)
217
292
  const policies = (await normalizeStoredDefinitionIn(db, ctx.orgId, row.definition))
218
293
  .toolPolicies;
219
- return overrideOf(policies, toolKey, policyKey);
294
+ return settingOf(policies, toolKey, policyKey);
220
295
  }
221
296
  /**
222
297
  * A grant matching the current definition hash and source connector, keyed by the call's policy key
@@ -14,7 +14,7 @@ export { BUILTIN_TOOL_NAMES } from "./builtin-names.js";
14
14
  export { canonicalJson } from "./canonical-json.js";
15
15
  /**
16
16
  * What a tool's `unavailable` is asked with: the org, and for a run's listing the root
17
- * conversation (`conversationId`; the sandbox tools follow its environment, ruling R8), whether
17
+ * conversation (`conversationId`; the sandbox tools follow its device, ruling R8), whether
18
18
  * the run is an eval replay (`replay`: its tool calls are never executed), and a `memo` shared by
19
19
  * the tools of one listing (one answer per question per listing).
20
20
  */
@@ -346,7 +346,7 @@ export declare class ToolRegistry {
346
346
  forkChild?: boolean;
347
347
  /**
348
348
  * The root conversation of the run (a sub-agent's tree root): tools whose availability
349
- * depends on the conversation's environment (the sandbox tools) are listed by it.
349
+ * depends on the conversation's device (the sandbox tools) are listed by it.
350
350
  */
351
351
  sandboxConversationId?: string;
352
352
  /** An eval replay's listing: its calls are never executed (the sandbox tools are listed). */
@@ -9,7 +9,7 @@ import { BackgroundModule } from "../background/background.module.js";
9
9
  import { ConversationAccessModule } from "../conversations/access.js";
10
10
  import { AttachmentsModule } from "../conversations/attachments.module.js";
11
11
  import { EventLogModule } from "../conversations/event-log.js";
12
- import { EnvironmentsModule } from "../environments/environments.module.js";
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
15
  import { ModelsModule } from "../models/models.module.js";
@@ -26,6 +26,7 @@ import { ApprovalsService } from "./approvals.service.js";
26
26
  import { Breaker } from "./breaker.js";
27
27
  import { BuiltinTools } from "./builtins/builtin-tools.js";
28
28
  import { WebPages } from "./builtins/web/page-cache.js";
29
+ import { ConnectorAccountPolicies } from "./connector-account-policies.js";
29
30
  import { ConnectorContent } from "./connector-content.js";
30
31
  import { ConnectorsService } from "./connectors.service.js";
31
32
  import { Credentials } from "./credentials.js";
@@ -58,9 +59,9 @@ ToolsModule = __decorate([
58
59
  OrgLockModule,
59
60
  // the `Agent` tool's dispatch
60
61
  SubagentsModule,
61
- // the builtin sandbox and skill tools run in the conversation's environment
62
+ // the builtin sandbox and skill tools run in the conversation's device
62
63
  SandboxModule,
63
- EnvironmentsModule,
64
+ DevicesModule,
64
65
  // `Bash` with run_in_background records its task; TaskOutput / TaskStop finish and stop it
65
66
  BackgroundModule,
66
67
  // the conversation page's tool metadata reads only what the viewer may see
@@ -86,6 +87,7 @@ ToolsModule = __decorate([
86
87
  ConnectorContent,
87
88
  ToolPipeline,
88
89
  ConnectorsService,
90
+ ConnectorAccountPolicies,
89
91
  OAuthFlow,
90
92
  ConversationToolInfo,
91
93
  OrgExtensionTools,
@@ -103,6 +105,7 @@ ToolsModule = __decorate([
103
105
  ConnectorContent,
104
106
  ToolPipeline,
105
107
  ConnectorsService,
108
+ ConnectorAccountPolicies,
106
109
  OAuthFlow,
107
110
  ConversationToolInfo,
108
111
  OrgExtensionTools,
@@ -0,0 +1,56 @@
1
+ import type { UiActionResult, UiView } from "@capybara-hq/plugin-sdk";
2
+ import type { FastifyReply, FastifyRequest } from "fastify";
3
+ import type { z } from "zod";
4
+ import { ApiTokenService } from "../identity/api-tokens.js";
5
+ import { type Principal } from "../identity/principal.js";
6
+ import { SessionService } from "../identity/sessions.js";
7
+ import { EmbedTokens } from "./embed-token.js";
8
+ import { ContributionParams, PlatformActionBody, PlatformViewQuery } from "./ui.schemas.js";
9
+ import { OrgUiStreams } from "./ui-stream.js";
10
+ import { type UiArrangementSlot, type UiContributionItem, UiViews } from "./views.js";
11
+ /**
12
+ * Extension UI in the admin console (UI slots spec §7, platform scope): the `platform.page`
13
+ * contributions, for super admins only (`@SuperAdmin`, a person: service tokens are refused), with
14
+ * no org. A platform contribution needs its extension running (no org enablement applies) and is
15
+ * given the platform's deployment config; its render args carry `scope: "platform"`, `role:
16
+ * "superAdmin"` and no `orgId`.
17
+ * - `GET /api/v1/admin/ui/contributions` → `{ items }` (`platform.page` only, base order);
18
+ * - `GET /api/v1/admin/ui/arrangement` → `{ slots }` over every arrangeable slot, the platform's
19
+ * entries only, with every running extension's contributions (hidden ones marked);
20
+ * - `GET /api/v1/admin/ui/views/:plugin/:id?locale=` → `{ view }`;
21
+ * - `POST /api/v1/admin/ui/actions/:plugin/:id` (`{ actionId, payload?, answer?, locale? }`) →
22
+ * `{ toast }` | `{ view }`;
23
+ * - `POST /api/v1/admin/ui/embeds/:plugin/:id/token` → `{ token, src, expiresAt, expiresIn }`: a
24
+ * platform embed token (`scope: "platform"`, `role: "superAdmin"`, no `orgId`), which
25
+ * `ctx.ui.verifyEmbedToken` accepts only for a `platform.page` contribution while the account is
26
+ * still an active super admin;
27
+ * - `GET /api/v1/admin/ui/stream`: the platform refresh SSE (`event: ui`, topic `platform-ui`),
28
+ * super admin re-checked on the `runtime.sseRecheckMs` interval.
29
+ * A contribution in any other slot is 404 `ui.not_found`, the same as one that does not exist.
30
+ */
31
+ export declare class AdminUiController {
32
+ private readonly views;
33
+ private readonly streams;
34
+ private readonly sessions;
35
+ private readonly tokens;
36
+ private readonly embeds;
37
+ constructor(views: UiViews, streams: OrgUiStreams, sessions: SessionService, tokens: ApiTokenService, embeds: EmbedTokens);
38
+ contributions(principal: Principal): Promise<{
39
+ items: UiContributionItem[];
40
+ }>;
41
+ arrangement(principal: Principal): Promise<{
42
+ slots: UiArrangementSlot[];
43
+ }>;
44
+ view(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>, q: z.infer<typeof PlatformViewQuery>): Promise<{
45
+ view: UiView;
46
+ }>;
47
+ action(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>, body: z.infer<typeof PlatformActionBody>): Promise<UiActionResult>;
48
+ embedToken(principal: Principal, { plugin, id }: z.infer<typeof ContributionParams>): Promise<{
49
+ token: string;
50
+ src: string;
51
+ expiresAt: string;
52
+ expiresIn: number;
53
+ }>;
54
+ /** The platform refresh stream: written on the raw response (`reply.hijack()`) like the org one. */
55
+ stream(principal: Principal, req: FastifyRequest, reply: FastifyReply): Promise<void>;
56
+ }
@@ -0,0 +1,192 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { Controller, Get, HttpCode, Inject, Post, Req, Res } from "@nestjs/common";
11
+ import { ApiTokenService } from "../identity/api-tokens.js";
12
+ import { credentialCheck } from "../identity/credential-check.js";
13
+ import { CurrentPrincipal, requireUser, SuperAdmin, } from "../identity/principal.js";
14
+ import { SessionService } from "../identity/sessions.js";
15
+ import { createZodDto } from "../infra/validation/create-zod-dto.js";
16
+ import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
17
+ import { EMBED_TOKEN_TTL_SECONDS, EmbedTokens } from "./embed-token.js";
18
+ import { ContributionParams, PlatformActionBody, PlatformViewQuery } from "./ui.schemas.js";
19
+ import { OrgUiStreams } from "./ui-stream.js";
20
+ import { BEARER_RE, embedSrc } from "./views.controller.js";
21
+ import { UiViews } from "./views.js";
22
+ class AdminUiContributionParamsDto extends createZodDto(ContributionParams, "AdminUiContributionParams") {
23
+ }
24
+ class AdminUiViewQueryDto extends createZodDto(PlatformViewQuery, "AdminUiViewQuery") {
25
+ }
26
+ class AdminUiActionBodyDto extends createZodDto(PlatformActionBody, "AdminUiActionBody") {
27
+ }
28
+ /**
29
+ * Extension UI in the admin console (UI slots spec §7, platform scope): the `platform.page`
30
+ * contributions, for super admins only (`@SuperAdmin`, a person: service tokens are refused), with
31
+ * no org. A platform contribution needs its extension running (no org enablement applies) and is
32
+ * given the platform's deployment config; its render args carry `scope: "platform"`, `role:
33
+ * "superAdmin"` and no `orgId`.
34
+ * - `GET /api/v1/admin/ui/contributions` → `{ items }` (`platform.page` only, base order);
35
+ * - `GET /api/v1/admin/ui/arrangement` → `{ slots }` over every arrangeable slot, the platform's
36
+ * entries only, with every running extension's contributions (hidden ones marked);
37
+ * - `GET /api/v1/admin/ui/views/:plugin/:id?locale=` → `{ view }`;
38
+ * - `POST /api/v1/admin/ui/actions/:plugin/:id` (`{ actionId, payload?, answer?, locale? }`) →
39
+ * `{ toast }` | `{ view }`;
40
+ * - `POST /api/v1/admin/ui/embeds/:plugin/:id/token` → `{ token, src, expiresAt, expiresIn }`: a
41
+ * platform embed token (`scope: "platform"`, `role: "superAdmin"`, no `orgId`), which
42
+ * `ctx.ui.verifyEmbedToken` accepts only for a `platform.page` contribution while the account is
43
+ * still an active super admin;
44
+ * - `GET /api/v1/admin/ui/stream`: the platform refresh SSE (`event: ui`, topic `platform-ui`),
45
+ * super admin re-checked on the `runtime.sseRecheckMs` interval.
46
+ * A contribution in any other slot is 404 `ui.not_found`, the same as one that does not exist.
47
+ */
48
+ let AdminUiController = class AdminUiController {
49
+ views;
50
+ streams;
51
+ sessions;
52
+ tokens;
53
+ embeds;
54
+ constructor(views, streams, sessions, tokens, embeds) {
55
+ this.views = views;
56
+ this.streams = streams;
57
+ this.sessions = sessions;
58
+ this.tokens = tokens;
59
+ this.embeds = embeds;
60
+ }
61
+ async contributions(principal) {
62
+ requireUser(principal);
63
+ return { items: await this.views.platformContributions() };
64
+ }
65
+ async arrangement(principal) {
66
+ requireUser(principal);
67
+ return { slots: await this.views.platformArrangementView() };
68
+ }
69
+ async view(principal, { plugin, id }, q) {
70
+ const p = requireUser(principal);
71
+ const view = await this.views.render({
72
+ scope: "platform",
73
+ accountId: p.account.id,
74
+ plugin,
75
+ id,
76
+ locale: q.locale,
77
+ });
78
+ return { view };
79
+ }
80
+ async action(principal, { plugin, id }, body) {
81
+ const p = requireUser(principal);
82
+ return this.views.act({ scope: "platform", accountId: p.account.id, plugin, id, locale: body.locale }, {
83
+ actionId: body.actionId,
84
+ ...(body.payload !== undefined && { payload: body.payload }),
85
+ ...(body.answer !== undefined && { answer: body.answer }),
86
+ });
87
+ }
88
+ async embedToken(principal, { plugin, id }) {
89
+ const p = requireUser(principal);
90
+ const target = await this.views.embed({
91
+ scope: "platform",
92
+ accountId: p.account.id,
93
+ plugin,
94
+ id,
95
+ });
96
+ const { token, expiresAt } = this.embeds.issue({
97
+ plugin: target.plugin,
98
+ scope: "platform",
99
+ accountId: p.account.id,
100
+ role: "superAdmin",
101
+ contribution: target.contribution,
102
+ });
103
+ return {
104
+ token,
105
+ src: embedSrc(target.plugin, target.path),
106
+ expiresAt: expiresAt.toISOString(),
107
+ expiresIn: EMBED_TOKEN_TTL_SECONDS,
108
+ };
109
+ }
110
+ /** The platform refresh stream: written on the raw response (`reply.hijack()`) like the org one. */
111
+ async stream(principal, req, reply) {
112
+ const p = requireUser(principal);
113
+ const res = reply.raw;
114
+ const handle = await this.streams.open({
115
+ scope: "platform",
116
+ viewerAccountId: p.account.id,
117
+ credential: credentialCheck(p, BEARER_RE.exec(req.headers.authorization ?? "")?.[1], {
118
+ sessions: this.sessions,
119
+ tokens: this.tokens,
120
+ }),
121
+ }, {
122
+ write: (chunk) => {
123
+ res.write(chunk);
124
+ },
125
+ backlog: () => res.writableLength,
126
+ end: () => {
127
+ res.end();
128
+ },
129
+ abort: () => {
130
+ res.destroy();
131
+ },
132
+ });
133
+ reply.hijack();
134
+ res.on("close", () => handle.close());
135
+ if (res.destroyed || !res.socket || res.socket.destroyed) {
136
+ handle.close();
137
+ res.destroy();
138
+ return;
139
+ }
140
+ res.writeHead(200, {
141
+ "content-type": "text/event-stream; charset=utf-8",
142
+ "cache-control": "no-cache",
143
+ "x-accel-buffering": "no",
144
+ connection: "keep-alive",
145
+ });
146
+ res.flushHeaders();
147
+ handle.begin();
148
+ }
149
+ };
150
+ __decorate([
151
+ Get("contributions"),
152
+ __param(0, CurrentPrincipal())
153
+ ], AdminUiController.prototype, "contributions", null);
154
+ __decorate([
155
+ Get("arrangement"),
156
+ __param(0, CurrentPrincipal())
157
+ ], AdminUiController.prototype, "arrangement", null);
158
+ __decorate([
159
+ Get("views/:plugin/:id"),
160
+ __param(0, CurrentPrincipal()),
161
+ __param(1, ZParam(AdminUiContributionParamsDto)),
162
+ __param(2, ZQuery(AdminUiViewQueryDto))
163
+ ], AdminUiController.prototype, "view", null);
164
+ __decorate([
165
+ Post("actions/:plugin/:id"),
166
+ HttpCode(200),
167
+ __param(0, CurrentPrincipal()),
168
+ __param(1, ZParam(AdminUiContributionParamsDto)),
169
+ __param(2, ZBody(AdminUiActionBodyDto))
170
+ ], AdminUiController.prototype, "action", null);
171
+ __decorate([
172
+ Post("embeds/:plugin/:id/token"),
173
+ HttpCode(200),
174
+ __param(0, CurrentPrincipal()),
175
+ __param(1, ZParam(AdminUiContributionParamsDto))
176
+ ], AdminUiController.prototype, "embedToken", null);
177
+ __decorate([
178
+ Get("stream"),
179
+ __param(0, CurrentPrincipal()),
180
+ __param(1, Req()),
181
+ __param(2, Res())
182
+ ], AdminUiController.prototype, "stream", null);
183
+ AdminUiController = __decorate([
184
+ Controller("api/v1/admin/ui"),
185
+ SuperAdmin(),
186
+ __param(0, Inject(UiViews)),
187
+ __param(1, Inject(OrgUiStreams)),
188
+ __param(2, Inject(SessionService)),
189
+ __param(3, Inject(ApiTokenService)),
190
+ __param(4, Inject(EmbedTokens))
191
+ ], AdminUiController);
192
+ export { AdminUiController };
@@ -0,0 +1,74 @@
1
+ import { type UiSlotId } from "@capybara-hq/plugin-sdk";
2
+ import { z } from "zod";
3
+ import type { UiContributionItem } from "./views.js";
4
+ export declare const UiArrangementEntrySchema: z.ZodObject<{
5
+ hidden: z.ZodArray<z.ZodString>;
6
+ order: z.ZodArray<z.ZodString>;
7
+ }, z.core.$strict>;
8
+ export type UiArrangementEntry = z.infer<typeof UiArrangementEntrySchema>;
9
+ export declare const UiArrangementSchema: z.ZodRecord<z.ZodEnum<{
10
+ "agent.detail.section": "agent.detail.section";
11
+ "conversation.panel": "conversation.panel";
12
+ "conversation.properties.section": "conversation.properties.section";
13
+ "conversation.tool-result.card": "conversation.tool-result.card";
14
+ "file.preview": "file.preview";
15
+ "org.page": "org.page";
16
+ "org.settings.section": "org.settings.section";
17
+ "platform.page": "platform.page";
18
+ }> & z.core.$partial, z.ZodObject<{
19
+ hidden: z.ZodArray<z.ZodString>;
20
+ order: z.ZodArray<z.ZodString>;
21
+ }, z.core.$strict>>;
22
+ export type UiArrangement = z.infer<typeof UiArrangementSchema>;
23
+ /** The slots an org arranges: every arrangeable slot but the platform's own. */
24
+ export declare const ORG_ARRANGEABLE: readonly UiSlotId[];
25
+ /** An org's patch: a slot's entry replaces the stored one, null removes it. */
26
+ export declare const OrgUiArrangementPatchSchema: z.ZodRecord<z.ZodEnum<{
27
+ "agent.detail.section": "agent.detail.section";
28
+ "conversation.panel": "conversation.panel";
29
+ "conversation.properties.section": "conversation.properties.section";
30
+ "conversation.tool-result.card": "conversation.tool-result.card";
31
+ "file.preview": "file.preview";
32
+ "org.page": "org.page";
33
+ "org.settings.section": "org.settings.section";
34
+ "platform.page": "platform.page";
35
+ }> & z.core.$partial, z.ZodNullable<z.ZodObject<{
36
+ hidden: z.ZodArray<z.ZodString>;
37
+ order: z.ZodArray<z.ZodString>;
38
+ }, z.core.$strict>>>;
39
+ /** A deployment patch: like {@link OrgUiArrangementPatchSchema} over every arrangeable slot. */
40
+ export declare const UiArrangementPatchSchema: z.ZodRecord<z.ZodEnum<{
41
+ "agent.detail.section": "agent.detail.section";
42
+ "conversation.panel": "conversation.panel";
43
+ "conversation.properties.section": "conversation.properties.section";
44
+ "conversation.tool-result.card": "conversation.tool-result.card";
45
+ "file.preview": "file.preview";
46
+ "org.page": "org.page";
47
+ "org.settings.section": "org.settings.section";
48
+ "platform.page": "platform.page";
49
+ }> & z.core.$partial, z.ZodNullable<z.ZodObject<{
50
+ hidden: z.ZodArray<z.ZodString>;
51
+ order: z.ZodArray<z.ZodString>;
52
+ }, z.core.$strict>>>;
53
+ /** Merges a per-slot patch into the stored arrangement (null removes the slot's entry). */
54
+ export declare function mergeArrangement(current: UiArrangement | undefined, patch: Partial<Record<UiSlotId, UiArrangementEntry | null>>): UiArrangement;
55
+ /** The entry that applies to a slot: the org's if it arranged the slot, else the platform's. */
56
+ export declare function effectiveEntry(org: UiArrangement | undefined, platform: UiArrangement, slot: UiSlotId): {
57
+ entry: UiArrangementEntry;
58
+ source: "org" | "platform" | "none";
59
+ };
60
+ /**
61
+ * Orders the items of one slot (hidden ones stay in place): grouped by anchor in the slot's anchor
62
+ * order, also without an entry (the lists match what the page renders per anchor); within a group
63
+ * the keys of `entry.order` come first in that order, the rest in base order. Unknown keys are
64
+ * ignored, so an order never moves an item across anchors.
65
+ */
66
+ export declare function orderItems(items: UiContributionItem[], entry: UiArrangementEntry): UiContributionItem[];
67
+ /** {@link orderItems} without the hidden keys. */
68
+ export declare function arrange(items: UiContributionItem[], entry: UiArrangementEntry): UiContributionItem[];
69
+ /**
70
+ * Applies the per-slot entries to a mixed list: each slot's items are arranged and written back
71
+ * into the positions that slot occupied (a slot without an entry: its base order, grouped by
72
+ * anchor).
73
+ */
74
+ export declare function arrangeBySlot(items: UiContributionItem[], entryOf: (slot: UiSlotId) => UiArrangementEntry): UiContributionItem[];