@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
@@ -1,7 +1,7 @@
1
1
  import { rootsOf, ToolError } from "@capybara-hq/plugin-sdk";
2
2
  import { decodeTail, tailCommand } from "../../../background/session.js";
3
3
  import { clip, display, mapSandboxError, sandboxBaseTimeoutMs, unavailableWhenNoSandbox, } from "./common.js";
4
- import { onTaskInstance, ownedTask, recheckEnvironment, statusLine, taskEnv, } from "./task-output.js";
4
+ import { onTaskInstance, ownedTask, recheckDevice, statusLine, taskEnv, } from "./task-output.js";
5
5
  /** The time limit of reading a stopped task's last lines. */
6
6
  const TAIL_EXEC_TIMEOUT_MS = 10_000;
7
7
  const SPEC = {
@@ -84,7 +84,7 @@ export function taskStopTool(deps) {
84
84
  const { task_id } = input;
85
85
  const env = await taskEnv(deps, ctx);
86
86
  const task = await ownedTask(env, ctx, task_id);
87
- await recheckEnvironment(env, ctx);
87
+ await recheckDevice(env, ctx);
88
88
  if (task.status !== "running") {
89
89
  return ended(env, task, "had already finished", ctx.signal);
90
90
  }
@@ -46,10 +46,10 @@ export function sandboxWriteTool(deps) {
46
46
  const data = new TextEncoder().encode(input.content);
47
47
  checkPathShape(input.file_path, "file_path");
48
48
  let path = "";
49
- const { value, notices } = await inSandbox(deps, ctx, ({ environmentId, h, roots, p, signal }) => {
49
+ const { value, notices } = await inSandbox(deps, ctx, ({ deviceId, h, roots, p, signal }) => {
50
50
  path = checkWritable(input.file_path, roots);
51
51
  // stat, read the previous content, write: one holder at a time per file
52
- return withPathLease(deps, { environmentId }, path, signal, async (signal) => {
52
+ return withPathLease(deps, { deviceId }, path, signal, async (signal) => {
53
53
  const st = await p.stat(h, path, signal);
54
54
  if (st?.isDir)
55
55
  throw new ToolError(isDirectory(path));
@@ -8,7 +8,7 @@ import { type SandboxToolDeps } from "./sandbox/common.js";
8
8
  * Builtin tool `Skill` (M5 T14, ruling M17): resolves the name against the latest skills catalog
9
9
  * snapshot in the log to an id and version, and returns that version's SKILL.md body and file list
10
10
  * (the result names the version). A skill with other files is copied whole into the conversation's
11
- * environment at `<skills root>/<name>@<digest>/` (the instance's skills root, `/skills` by default; the provider writes as root, commands read only; the
11
+ * device at `<skills root>/<name>@<digest>/` (the instance's skills root, `/skills` by default; the provider writes as root, commands read only; the
12
12
  * digest is {@link skillDigest} of the version's file manifest, so versions of one skill coexist in
13
13
  * the shared file system). Without a sandbox provider only the body is given, with a note that
14
14
  * scripts are unavailable.
@@ -14,7 +14,7 @@ import { clip, display, inSandbox, sandboxBaseTimeoutMs, sandboxConversationOf,
14
14
  * Builtin tool `Skill` (M5 T14, ruling M17): resolves the name against the latest skills catalog
15
15
  * snapshot in the log to an id and version, and returns that version's SKILL.md body and file list
16
16
  * (the result names the version). A skill with other files is copied whole into the conversation's
17
- * environment at `<skills root>/<name>@<digest>/` (the instance's skills root, `/skills` by default; the provider writes as root, commands read only; the
17
+ * device at `<skills root>/<name>@<digest>/` (the instance's skills root, `/skills` by default; the provider writes as root, commands read only; the
18
18
  * digest is {@link skillDigest} of the version's file manifest, so versions of one skill coexist in
19
19
  * the shared file system). Without a sandbox provider only the body is given, with a note that
20
20
  * scripts are unavailable.
@@ -49,7 +49,7 @@ const SPEC = {
49
49
  description: "Load a skill from the skills list: returns the skill's instructions " +
50
50
  "(its SKILL.md) and the list of its files. Call it before starting a task that matches a " +
51
51
  "skill's description, then follow the returned instructions. If the skill has scripts or other " +
52
- "files, they are copied read-only to <name>@<version-id>/ in the skills directory of this environment; run scripts " +
52
+ "files, they are copied read-only to <name>@<version-id>/ in the skills directory of this device; run scripts " +
53
53
  "there with an interpreter (sh, python3, node). Call it again when the skills list changes. Pass args when the task gives the skill arguments (they replace " +
54
54
  "$ARGUMENTS and $1, $2… in the instructions). Do not use it for skills that are not in the list.",
55
55
  inputSchema: {
@@ -0,0 +1,51 @@
1
+ import { KernelDb } from "../infra/database/kernel-db.js";
2
+ import { TransactionHost } from "../infra/database/transaction-host.js";
3
+ import { OrgLock } from "../orgs/org-lock.js";
4
+ import { type ToolPolicy } from "./registry.js";
5
+ /**
6
+ * Why a member cannot set a tool's policy: the org switched the tool off (or it is not offered:
7
+ * pending review, gone), or the org set its default to deny. Either way it stays denied.
8
+ */
9
+ export type MemberPolicyLock = "disabled" | "org_deny";
10
+ /** One tool on the member's 权限 sheet (`GET …/connectors/:id/tool-policies/me`). */
11
+ export interface MemberToolPolicyView {
12
+ name: string;
13
+ key: string;
14
+ title: string | null;
15
+ description: string;
16
+ /** The org's default for the tool (`connector_tools.default_policy`): what applies without a setting. */
17
+ defaultPolicy: ToolPolicy;
18
+ /** What the tool's annotations alone give (`readOnlyHint: true` allows, anything else asks). */
19
+ annotationPolicy: "allow" | "ask";
20
+ /** The member's own setting, null without one. */
21
+ policy: ToolPolicy | null;
22
+ /** Locked by the org: the member's setting does not apply, the tool is denied. */
23
+ locked: MemberPolicyLock | null;
24
+ }
25
+ /** Whether a connector's credential is per member: its calls are made with the member's connection. */
26
+ export declare const personalAuth: (auth: unknown) => boolean;
27
+ /**
28
+ * A member's own tool policies for their own connection of a personal-credential connector
29
+ * (maintainer decision 2026-11-09, the connector card's 权限). They apply to every agent the
30
+ * member uses; an agent's tool policy overrides them; a tool the org switched off or set to deny
31
+ * stays denied (`tools/policy.ts` `connectorPolicy`). Only the account itself reads or writes its
32
+ * rows: the routes take the account from the session, never from the request.
33
+ */
34
+ export declare class ConnectorAccountPolicies {
35
+ private readonly kdb;
36
+ private readonly orgLock;
37
+ readonly txHost: TransactionHost;
38
+ constructor(kdb: KernelDb, orgLock: OrgLock, txHost: TransactionHost);
39
+ /** The kernel-schema handle of the current context (its transaction, else the pool). */
40
+ private get db();
41
+ /** The connector's tools with the account's settings; not personal -> 409 `connector.not_personal`. */
42
+ read(orgId: string, connectorId: string, accountId: string): Promise<MemberToolPolicyView[]>;
43
+ /**
44
+ * Sets (a policy) or clears (null) the account's policies for some of the connector's tools;
45
+ * an unknown tool -> 404 `connector.tool_not_found`. Locked tools may hold a setting: it does not
46
+ * apply while the lock lasts. Audits `connector.member_policies_changed`.
47
+ */
48
+ set(orgId: string, connectorId: string, accountId: string, changes: Record<string, ToolPolicy | null>, actorTokenId?: string): Promise<void>;
49
+ /** The org's live personal-credential connector, else 404 / 409. */
50
+ private connector;
51
+ }
@@ -0,0 +1,165 @@
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 { connectorToolKey } from "@capybara-hq/plugin-sdk";
11
+ import { Inject, Injectable } from "@nestjs/common";
12
+ import { sql } from "kysely";
13
+ import { ApiError } from "../errors.js";
14
+ import { audit } from "../identity/audit.js";
15
+ import { KernelDb } from "../infra/database/kernel-db.js";
16
+ import { TransactionHost } from "../infra/database/transaction-host.js";
17
+ import { Transactional } from "../infra/database/transactional.decorator.js";
18
+ import { OrgLock } from "../orgs/org-lock.js";
19
+ import { policyFromAnnotations } from "./registry.js";
20
+ /** Whether a connector's credential is per member: its calls are made with the member's connection. */
21
+ export const personalAuth = (auth) => {
22
+ const a = auth;
23
+ return a !== null && a.type !== "none" && a.ownership === "personal";
24
+ };
25
+ const notPersonal = () => new ApiError("connector.not_personal", "This connector uses one credential for the whole organization; only a connector each member connects to with their own credential has per-member tool policies");
26
+ const notFound = () => new ApiError("connector.not_found", "Connector not found");
27
+ /**
28
+ * A member's own tool policies for their own connection of a personal-credential connector
29
+ * (maintainer decision 2026-11-09, the connector card's 权限). They apply to every agent the
30
+ * member uses; an agent's tool policy overrides them; a tool the org switched off or set to deny
31
+ * stays denied (`tools/policy.ts` `connectorPolicy`). Only the account itself reads or writes its
32
+ * rows: the routes take the account from the session, never from the request.
33
+ */
34
+ let ConnectorAccountPolicies = class ConnectorAccountPolicies {
35
+ kdb;
36
+ orgLock;
37
+ txHost;
38
+ constructor(kdb, orgLock, txHost) {
39
+ this.kdb = kdb;
40
+ this.orgLock = orgLock;
41
+ this.txHost = txHost;
42
+ }
43
+ /** The kernel-schema handle of the current context (its transaction, else the pool). */
44
+ get db() {
45
+ return this.kdb.db;
46
+ }
47
+ /** The connector's tools with the account's settings; not personal -> 409 `connector.not_personal`. */
48
+ async read(orgId, connectorId, accountId) {
49
+ const c = await this.connector(orgId, connectorId);
50
+ const tools = await this.db
51
+ .selectFrom("connector_tools")
52
+ .select(["name", "spec", "status", "default_policy", "missing_since"])
53
+ .where("connector_id", "=", connectorId)
54
+ .orderBy("name")
55
+ .execute();
56
+ const mine = new Map((await this.db
57
+ .selectFrom("connector_tool_account_policies")
58
+ .select(["tool_name", "policy"])
59
+ .where("connector_id", "=", connectorId)
60
+ .where("account_id", "=", accountId)
61
+ .execute()).map((r) => [r.tool_name, r.policy]));
62
+ return tools.map((t) => {
63
+ const spec = t.spec;
64
+ const locked = t.status !== "enabled" || t.missing_since !== null
65
+ ? "disabled"
66
+ : t.default_policy === "deny"
67
+ ? "org_deny"
68
+ : null;
69
+ return {
70
+ name: t.name,
71
+ key: connectorToolKey(c.slug, t.name),
72
+ title: typeof spec.title === "string" ? spec.title : null,
73
+ description: spec.description,
74
+ defaultPolicy: t.default_policy,
75
+ annotationPolicy: policyFromAnnotations(spec.annotations),
76
+ policy: mine.get(t.name) ?? null,
77
+ locked,
78
+ };
79
+ });
80
+ }
81
+ /**
82
+ * Sets (a policy) or clears (null) the account's policies for some of the connector's tools;
83
+ * an unknown tool -> 404 `connector.tool_not_found`. Locked tools may hold a setting: it does not
84
+ * apply while the lock lasts. Audits `connector.member_policies_changed`.
85
+ */
86
+ async set(orgId, connectorId, accountId, changes, actorTokenId) {
87
+ await this.orgLock.lockAndAuthorize(orgId, accountId, "connectors.use");
88
+ await this.connector(orgId, connectorId);
89
+ const names = Object.keys(changes);
90
+ if (names.length === 0)
91
+ return;
92
+ const known = new Set((await this.db
93
+ .selectFrom("connector_tools")
94
+ .select("name")
95
+ .where("connector_id", "=", connectorId)
96
+ .where("name", "in", names)
97
+ .execute()).map((r) => r.name));
98
+ const unknown = names.find((n) => !known.has(n));
99
+ if (unknown !== undefined) {
100
+ throw new ApiError("connector.tool_not_found", `No tool ${unknown} in this connector`);
101
+ }
102
+ const cleared = names.filter((n) => changes[n] === null);
103
+ if (cleared.length > 0) {
104
+ await this.db
105
+ .deleteFrom("connector_tool_account_policies")
106
+ .where("connector_id", "=", connectorId)
107
+ .where("account_id", "=", accountId)
108
+ .where("tool_name", "in", cleared)
109
+ .execute();
110
+ }
111
+ const set = names.flatMap((n) => {
112
+ const p = changes[n];
113
+ return p === null || p === undefined ? [] : [{ name: n, policy: p }];
114
+ });
115
+ if (set.length > 0) {
116
+ await this.db
117
+ .insertInto("connector_tool_account_policies")
118
+ .values(set.map((x) => ({
119
+ org_id: orgId,
120
+ connector_id: connectorId,
121
+ account_id: accountId,
122
+ tool_name: x.name,
123
+ policy: x.policy,
124
+ })))
125
+ .onConflict((oc) => oc.columns(["connector_id", "account_id", "tool_name"]).doUpdateSet({
126
+ policy: (eb) => eb.ref("excluded.policy"),
127
+ updated_at: sql `now()`,
128
+ }))
129
+ .execute();
130
+ }
131
+ await audit(this.db, {
132
+ actorAccountId: accountId,
133
+ ...(actorTokenId !== undefined && { actorTokenId }),
134
+ orgId,
135
+ action: "connector.member_policies_changed",
136
+ target: connectorId,
137
+ detail: { tools: changes },
138
+ });
139
+ }
140
+ /** The org's live personal-credential connector, else 404 / 409. */
141
+ async connector(orgId, connectorId) {
142
+ const c = await this.db
143
+ .selectFrom("connectors")
144
+ .select(["slug", "auth"])
145
+ .where("id", "=", connectorId)
146
+ .where("org_id", "=", orgId)
147
+ .where("deleted_at", "is", null)
148
+ .executeTakeFirst();
149
+ if (!c)
150
+ throw notFound();
151
+ if (!personalAuth(c.auth))
152
+ throw notPersonal();
153
+ return { slug: c.slug };
154
+ }
155
+ };
156
+ __decorate([
157
+ Transactional()
158
+ ], ConnectorAccountPolicies.prototype, "set", null);
159
+ ConnectorAccountPolicies = __decorate([
160
+ Injectable(),
161
+ __param(0, Inject(KernelDb)),
162
+ __param(1, Inject(OrgLock)),
163
+ __param(2, Inject(TransactionHost))
164
+ ], ConnectorAccountPolicies);
165
+ export { ConnectorAccountPolicies };
@@ -1,7 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { PluginIcons } from "../extensions/plugin-icons.js";
2
3
  import { type Principal } from "../identity/principal.js";
3
4
  import { type OrgContext } from "../orgs/org.guard.js";
4
- import { CredentialValue } from "./connectors.schemas.js";
5
+ import { ConnectorAccountPolicies, type MemberToolPolicyView } from "./connector-account-policies.js";
6
+ import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
5
7
  import { type ConnectorDetail, type ConnectorPreview, type ConnectorSummary, ConnectorsService, type ToolIssue } from "./connectors.service.js";
6
8
  declare const CreateBody: z.ZodObject<{
7
9
  name: z.ZodString;
@@ -173,9 +175,19 @@ declare const UpdateBody: z.ZodObject<{
173
175
  */
174
176
  export declare class ConnectorsController {
175
177
  private readonly connectors;
176
- constructor(connectors: ConnectorsService);
178
+ private readonly icons;
179
+ private readonly memberPolicies;
180
+ constructor(connectors: ConnectorsService, icons: PluginIcons, memberPolicies: ConnectorAccountPolicies);
181
+ /**
182
+ * Each connector with `icon`: the icon of the plugin its template came from, on the org's catalog
183
+ * route (null for a custom connector, a plugin without an icon, or one the reader's catalog does
184
+ * not show: the share gate holds; maintainer decision 2026-11-09).
185
+ */
186
+ private withIcons;
177
187
  list(org: OrgContext): Promise<{
178
- connectors: ConnectorSummary[];
188
+ connectors: (ConnectorSummary & {
189
+ icon: string | null;
190
+ })[];
179
191
  }>;
180
192
  /**
181
193
  * The connector kinds the org can create connectors of, each with the plugin providing it (the
@@ -197,7 +209,9 @@ export declare class ConnectorsController {
197
209
  */
198
210
  preview(principal: Principal, org: OrgContext, body: z.infer<typeof PreviewBody>): Promise<ConnectorPreview>;
199
211
  get(principal: Principal, org: OrgContext, connectorId: string): Promise<{
200
- connector: ConnectorDetail;
212
+ connector: ConnectorDetail & {
213
+ icon: string | null;
214
+ };
201
215
  }>;
202
216
  update(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof UpdateBody>): Promise<{
203
217
  connector: ConnectorDetail;
@@ -218,6 +232,23 @@ export declare class ConnectorsController {
218
232
  }>;
219
233
  setOrgCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
220
234
  setMyCredential(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof CredentialValue>): Promise<void>;
235
+ /**
236
+ * The caller's own tool policies for their own connection of a personal-credential connector
237
+ * (the card's 权限, decision 2026-11-09): each tool with the org default, the annotation default,
238
+ * the caller's setting and whether the org locked it. Only ever the caller's rows. A connector
239
+ * with one org credential -> 409 `connector.not_personal`.
240
+ */
241
+ myToolPolicies(principal: Principal, org: OrgContext, connectorId: string): Promise<{
242
+ tools: MemberToolPolicyView[];
243
+ }>;
244
+ /**
245
+ * Sets (`allow` / `ask` / `deny`) or clears (null) the caller's own policies for some tools:
246
+ * `{ tools: { <name>: policy | null } }` -> the sheet again. Unknown tool -> 404
247
+ * `connector.tool_not_found`.
248
+ */
249
+ setMyToolPolicies(principal: Principal, org: OrgContext, connectorId: string, body: z.infer<typeof MemberToolPoliciesBody>): Promise<{
250
+ tools: MemberToolPolicyView[];
251
+ }>;
221
252
  clearMyCredential(principal: Principal, org: OrgContext, connectorId: string): Promise<void>;
222
253
  }
223
254
  export {};
@@ -10,12 +10,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { ConnectorAuthSchema } from "@capybara-hq/plugin-sdk";
11
11
  import { Controller, Delete, Get, HttpCode, Inject, Param, Patch, Post, Put } from "@nestjs/common";
12
12
  import { z } from "zod";
13
+ import { orgIconScope, PluginIcons } from "../extensions/plugin-icons.js";
13
14
  import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
14
15
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
15
16
  import { ZBody } from "../infra/validation/zod-params.js";
16
17
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
17
18
  import { can } from "../orgs/roles.js";
18
- import { CredentialValue } from "./connectors.schemas.js";
19
+ import { ConnectorAccountPolicies, } from "./connector-account-policies.js";
20
+ import { CredentialValue, MemberToolPoliciesBody } from "./connectors.schemas.js";
19
21
  import { ConnectorsService, } from "./connectors.service.js";
20
22
  import { publicEndpoint } from "./endpoint.js";
21
23
  /**
@@ -84,17 +86,37 @@ class ConnectorToolReviewBody extends createZodDto(ReviewBody, "ConnectorToolRev
84
86
  }
85
87
  class ConnectorCredentialBody extends createZodDto(CredentialValue, "ConnectorCredentialBody") {
86
88
  }
89
+ class MemberToolPoliciesDto extends createZodDto(MemberToolPoliciesBody, "MemberToolPoliciesBody") {
90
+ }
87
91
  /**
88
92
  * 连接器(`/api/v1/orgs/:orgId/connectors`):读与个人凭据用 `connectors.use`(访客起),安装 / 修改 / 删除 / 刷新 /
89
93
  * 组织凭据用 `connectors.manage`(管理员起);两者都仅限用户主体。
90
94
  */
91
95
  let ConnectorsController = class ConnectorsController {
92
96
  connectors;
93
- constructor(connectors) {
97
+ icons;
98
+ memberPolicies;
99
+ constructor(connectors, icons, memberPolicies) {
94
100
  this.connectors = connectors;
101
+ this.icons = icons;
102
+ this.memberPolicies = memberPolicies;
103
+ }
104
+ /**
105
+ * Each connector with `icon`: the icon of the plugin its template came from, on the org's catalog
106
+ * route (null for a custom connector, a plugin without an icon, or one the reader's catalog does
107
+ * not show: the share gate holds; maintainer decision 2026-11-09).
108
+ */
109
+ async withIcons(org, rows) {
110
+ const ids = rows.flatMap((r) => (r.catalogItemId === null ? [] : [r.catalogItemId]));
111
+ const urls = await this.icons.ofItems(ids, orgIconScope(org));
112
+ return rows.map((r) => ({
113
+ ...r,
114
+ icon: r.catalogItemId === null ? null : (urls.get(r.catalogItemId) ?? null),
115
+ }));
95
116
  }
96
117
  async list(org) {
97
- return { connectors: (await this.connectors.list(org.orgId)).map((a) => forViewer(org, a)) };
118
+ const rows = (await this.connectors.list(org.orgId)).map((a) => forViewer(org, a));
119
+ return { connectors: await this.withIcons(org, rows) };
98
120
  }
99
121
  /**
100
122
  * The connector kinds the org can create connectors of, each with the plugin providing it (the
@@ -117,9 +139,9 @@ let ConnectorsController = class ConnectorsController {
117
139
  }
118
140
  async get(principal, org, connectorId) {
119
141
  const p = requireUser(principal);
120
- return {
121
- connector: forViewer(org, await this.connectors.get(org.orgId, connectorId, p.account.id)),
122
- };
142
+ const c = forViewer(org, await this.connectors.get(org.orgId, connectorId, p.account.id));
143
+ const [connector] = await this.withIcons(org, [c]);
144
+ return { connector: connector };
123
145
  }
124
146
  async update(principal, org, connectorId, body) {
125
147
  const p = requireUser(principal);
@@ -150,6 +172,26 @@ let ConnectorsController = class ConnectorsController {
150
172
  const p = requireUser(principal);
151
173
  await this.connectors.setCredential(org.orgId, connectorId, p.account.id, "personal", body, patTokenId(p));
152
174
  }
175
+ /**
176
+ * The caller's own tool policies for their own connection of a personal-credential connector
177
+ * (the card's 权限, decision 2026-11-09): each tool with the org default, the annotation default,
178
+ * the caller's setting and whether the org locked it. Only ever the caller's rows. A connector
179
+ * with one org credential -> 409 `connector.not_personal`.
180
+ */
181
+ async myToolPolicies(principal, org, connectorId) {
182
+ const p = requireUser(principal);
183
+ return { tools: await this.memberPolicies.read(org.orgId, connectorId, p.account.id) };
184
+ }
185
+ /**
186
+ * Sets (`allow` / `ask` / `deny`) or clears (null) the caller's own policies for some tools:
187
+ * `{ tools: { <name>: policy | null } }` -> the sheet again. Unknown tool -> 404
188
+ * `connector.tool_not_found`.
189
+ */
190
+ async setMyToolPolicies(principal, org, connectorId, body) {
191
+ const p = requireUser(principal);
192
+ await this.memberPolicies.set(org.orgId, connectorId, p.account.id, body.tools, patTokenId(p));
193
+ return { tools: await this.memberPolicies.read(org.orgId, connectorId, p.account.id) };
194
+ }
153
195
  async clearMyCredential(principal, org, connectorId) {
154
196
  const p = requireUser(principal);
155
197
  await this.connectors.clearMyCredential(org.orgId, connectorId, p.account.id, patTokenId(p));
@@ -239,6 +281,21 @@ __decorate([
239
281
  __param(2, Param("connectorId")),
240
282
  __param(3, ZBody(ConnectorCredentialBody))
241
283
  ], ConnectorsController.prototype, "setMyCredential", null);
284
+ __decorate([
285
+ Get(":connectorId/tool-policies/me"),
286
+ OrgAction("connectors.use"),
287
+ __param(0, CurrentPrincipal()),
288
+ __param(1, CurrentOrg()),
289
+ __param(2, Param("connectorId"))
290
+ ], ConnectorsController.prototype, "myToolPolicies", null);
291
+ __decorate([
292
+ Put(":connectorId/tool-policies/me"),
293
+ OrgAction("connectors.use"),
294
+ __param(0, CurrentPrincipal()),
295
+ __param(1, CurrentOrg()),
296
+ __param(2, Param("connectorId")),
297
+ __param(3, ZBody(MemberToolPoliciesDto))
298
+ ], ConnectorsController.prototype, "setMyToolPolicies", null);
242
299
  __decorate([
243
300
  Delete(":connectorId/credentials/me"),
244
301
  HttpCode(204),
@@ -249,6 +306,8 @@ __decorate([
249
306
  ], ConnectorsController.prototype, "clearMyCredential", null);
250
307
  ConnectorsController = __decorate([
251
308
  Controller("api/v1/orgs/:orgId/connectors"),
252
- __param(0, Inject(ConnectorsService))
309
+ __param(0, Inject(ConnectorsService)),
310
+ __param(1, Inject(PluginIcons)),
311
+ __param(2, Inject(ConnectorAccountPolicies))
253
312
  ], ConnectorsController);
254
313
  export { ConnectorsController };
@@ -10,3 +10,14 @@ export declare const ConnectorPathParams: z.ZodObject<{
10
10
  connectorId: z.ZodString;
11
11
  }, z.core.$strip>;
12
12
  export declare const CONNECTOR_ROUTES: readonly RouteSchemas[];
13
+ /**
14
+ * `PUT …/connectors/:connectorId/tool-policies/me`: the caller's own policy per tool name (null
15
+ * clears it; decision 2026-11-09).
16
+ */
17
+ export declare const MemberToolPoliciesBody: z.ZodObject<{
18
+ tools: z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodEnum<{
19
+ allow: "allow";
20
+ ask: "ask";
21
+ deny: "deny";
22
+ }>>>;
23
+ }, z.core.$strict>;
@@ -17,3 +17,14 @@ export const CONNECTOR_ROUTES = [
17
17
  params: ConnectorPathParams,
18
18
  },
19
19
  ];
20
+ /**
21
+ * `PUT …/connectors/:connectorId/tool-policies/me`: the caller's own policy per tool name (null
22
+ * clears it; decision 2026-11-09).
23
+ */
24
+ export const MemberToolPoliciesBody = z
25
+ .object({
26
+ tools: z
27
+ .record(z.string().min(1).max(200), z.enum(["allow", "ask", "deny"]).nullable())
28
+ .refine((v) => Object.keys(v).length <= 1000, { message: "Too many tools" }),
29
+ })
30
+ .strict();
@@ -66,6 +66,8 @@ export interface ConnectorSummary {
66
66
  * a custom one: the web offers it for a pending connector of that template (ruling P4-7).
67
67
  */
68
68
  platformTemplateId: string | null;
69
+ /** The plugin (catalog item) its template came from (`connectors.catalog_item_id`), else null. */
70
+ catalogItemId: string | null;
69
71
  createdBy: string;
70
72
  createdAt: Date;
71
73
  updatedAt: Date;
@@ -760,6 +760,7 @@ let ConnectorsService = class ConnectorsService {
760
760
  auth: r.auth,
761
761
  status: r.status,
762
762
  platformTemplateId: r.platform_template_id ?? null,
763
+ catalogItemId: r.catalog_item_id ?? null,
763
764
  createdBy: r.created_by,
764
765
  createdAt: r.created_at,
765
766
  updatedAt: r.updated_at,
@@ -65,7 +65,7 @@ export interface AskRequest {
65
65
  * policy, a fixed ask (R3, `requiresUserInteraction`), the org's `sandboxToolPolicy`, or the tool's
66
66
  * own default. `org` and `fixed` were added after the first B events (older logs say `default`).
67
67
  */
68
- export type ApprovalPolicySource = "agent" | "connector" | "default" | "role" | "org" | "fixed";
68
+ export type ApprovalPolicySource = "agent" | "connector" | "member" | "default" | "role" | "org" | "fixed";
69
69
  /**
70
70
  * `unavailable`:调用那一刻工具已不可用(连接器 / 工具不存在、停用、删除、待审核),值为事实原因;
71
71
  * 管线写「工具不可用」说明,而不是「策略拒绝」。
@@ -207,9 +207,10 @@ let ToolPipeline = class ToolPipeline {
207
207
  },
208
208
  effective,
209
209
  switches,
210
- inlineViews: (plugin) => new Set((host?.current(plugin)?.manifest.ui ?? [])
211
- .filter((c) => c.place === "inline" && c.aliasOf === undefined)
212
- .map((c) => c.id)),
210
+ inlineViews: (plugin) => new Set((host?.current(plugin)?.ui ?? [])
211
+ .filter((r) => r.slot.id === "conversation.tool-result.card" &&
212
+ r.contribution.aliasOf === undefined)
213
+ .map((r) => r.contribution.id)),
213
214
  onToolsChanged: (orgId, connectorId) => {
214
215
  void drift?.onToolsChanged(orgId, connectorId);
215
216
  },
@@ -25,6 +25,28 @@ export declare function decide(input: PolicyInput): ToolPolicy;
25
25
  * 除非 agent 当前版本的 `skills.writePolicy` 为 `ask`(`skillWritePolicy`);不知道可见性时按共享处理(更严)。
26
26
  */
27
27
  export declare function isFixedAsk(tool: ResolvedTool, visibility?: "private" | "org" | "members" | undefined, skillWritePolicy?: "allow" | "ask" | undefined): boolean;
28
+ /** Which level decided a connector tool's policy ({@link connectorPolicy}). */
29
+ export type ConnectorPolicySource = "org" | "member" | "agent";
30
+ /**
31
+ * A personal-credential connector tool's policy for one call (maintainer decisions 2026-11-09;
32
+ * an org-credential connector's tool keeps the org default with the agent's tightening only),
33
+ * highest first:
34
+ * 1. the org's default `deny` (a tool the org switched off never gets here: it is unavailable);
35
+ * 2. the member's own `deny` for calls with their connection (`member`): a floor no agent lifts;
36
+ * 3. the agent's setting (`agent`, from its tools page): `allow` / `ask` / `deny`;
37
+ * 4. the member's own `allow` / `ask`;
38
+ * 5. the org's default (`connector_tools.default_policy`, the annotation default unless an org
39
+ * admin changed it: `readOnlyHint: true` allows, anything else asks).
40
+ * Fixed asks (R3, `requiresUserInteraction`), role escalation and grants apply after this.
41
+ */
42
+ export declare function connectorPolicy(input: {
43
+ orgDefault: ToolPolicy;
44
+ member?: ToolPolicy | undefined;
45
+ agent?: ToolPolicy | undefined;
46
+ }): {
47
+ policy: ToolPolicy;
48
+ source: ConnectorPolicySource;
49
+ };
28
50
  /**
29
51
  * 执行时的策略判定(spec 3.6 R5:在动作执行那一刻判定)。每次调用都实时读取:
30
52
  * - 沙箱工具(bash / write / edit / deliver)的组织设置 `sandboxToolPolicy`(为 ask 时组织默认至少 ask);
@@ -36,7 +58,7 @@ export declare function isFixedAsk(tool: ResolvedTool, visibility?: "private" |
36
58
  * parent's current version still tightens it (sub-agents spec §5.3, §9); its "this conversation"
37
59
  * grants are the root conversation's (agent map spec §7); an org agent sub-agent (§5.5): `ctx.agentId` is the
38
60
  * dispatched agent (its current version, its "always" grants) and the parent agent's current
39
- * policies apply on top ({@link parentAgentOverride});
61
+ * policies apply on top ({@link parentAgentSetting});
40
62
  * - 授权表(`conversation_tool_grants` / `tool_grants`),`spec_hash` 与当前定义不一致视为无效(D8),
41
63
  * `connector_id` 与工具的来源连接器不一致也无效(删除后用同一 slug 重新安装的连接器不继承旧授权);
42
64
  * - 工具声明了 `requiredRole`(R1)时,发起人在组织中的当前角色:不是成员 → 拒绝;角色不足 → 按角色升级的审批