@capybara-hq/kernel 0.1.0-rc.10 → 0.1.0-rc.12

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 (106) hide show
  1. package/dist/admin/admin.schemas.d.ts +3 -0
  2. package/dist/agents/agents.schemas.d.ts +3 -0
  3. package/dist/agents/agents.service.js +11 -1
  4. package/dist/agents/definition.d.ts +1 -0
  5. package/dist/agents/definition.js +7 -3
  6. package/dist/agents/org-templates.js +4 -0
  7. package/dist/agents/pending-connectors.service.js +1 -1
  8. package/dist/catalog/catalog-content.d.ts +33 -1
  9. package/dist/catalog/catalog-content.js +65 -2
  10. package/dist/catalog/catalog-items.js +1 -0
  11. package/dist/catalog/catalog-scope.service.d.ts +29 -3
  12. package/dist/catalog/catalog-scope.service.js +82 -10
  13. package/dist/catalog/catalog.service.d.ts +7 -5
  14. package/dist/catalog/catalog.service.js +26 -5
  15. package/dist/catalog/claude/translate.js +3 -0
  16. package/dist/catalog/component-scope.controller.d.ts +18 -6
  17. package/dist/catalog/component-scope.controller.js +36 -13
  18. package/dist/catalog/component-scope.schemas.d.ts +4 -0
  19. package/dist/catalog/component-scope.service.d.ts +59 -9
  20. package/dist/catalog/component-scope.service.js +156 -27
  21. package/dist/catalog/component-state.d.ts +20 -3
  22. package/dist/catalog/component-state.js +40 -14
  23. package/dist/catalog/marketplaces.service.d.ts +1 -1
  24. package/dist/catalog/org-catalog.service.js +3 -0
  25. package/dist/catalog/owned-items.js +7 -0
  26. package/dist/catalog/platform-components.d.ts +23 -3
  27. package/dist/catalog/platform-components.js +118 -9
  28. package/dist/catalog/platform-migration.js +2 -1
  29. package/dist/catalog/plugin-import.d.ts +24 -4
  30. package/dist/catalog/plugin-import.js +41 -7
  31. package/dist/catalog/plugin-install.d.ts +20 -1
  32. package/dist/catalog/plugin-install.js +71 -1
  33. package/dist/catalog/plugin-package.d.ts +41 -4
  34. package/dist/catalog/plugin-package.js +127 -7
  35. package/dist/catalog/plugin-upgrade.d.ts +13 -2
  36. package/dist/catalog/plugin-upgrade.js +124 -6
  37. package/dist/catalog/unshare.js +1 -0
  38. package/dist/conversations/conversations.service.d.ts +9 -0
  39. package/dist/conversations/conversations.service.js +9 -2
  40. package/dist/conversations/events.d.ts +1 -0
  41. package/dist/db/kernel-schema.d.ts +54 -4
  42. package/dist/errors.d.ts +27 -0
  43. package/dist/errors.js +27 -0
  44. package/dist/http/api.module.js +7 -0
  45. package/dist/identity/accounts.d.ts +11 -4
  46. package/dist/identity/accounts.js +23 -10
  47. package/dist/identity/me.controller.d.ts +10 -10
  48. package/dist/identity/me.controller.js +8 -6
  49. package/dist/identity/me.schemas.d.ts +13 -3
  50. package/dist/identity/me.schemas.js +10 -2
  51. package/dist/identity/settings.d.ts +27 -0
  52. package/dist/identity/settings.js +64 -0
  53. package/dist/orgs/settings.service.d.ts +31 -0
  54. package/dist/orgs/settings.service.js +31 -1
  55. package/dist/platform/exclusions.js +26 -0
  56. package/dist/platform/operation.js +6 -2
  57. package/dist/platform/ops/admin.ops.d.ts +3 -1
  58. package/dist/platform/ops/admin.ops.js +25 -7
  59. package/dist/platform/ops/agents.ops.js +19 -6
  60. package/dist/platform/ops/components.ops.js +1 -0
  61. package/dist/platform/ops/org-settings.ops.d.ts +3 -1
  62. package/dist/platform/ops/org-settings.ops.js +33 -8
  63. package/dist/platform/ops/orgs.ops.js +1 -0
  64. package/dist/platform/ops/platform.ops.d.ts +3 -1
  65. package/dist/platform/ops/platform.ops.js +43 -16
  66. package/dist/platform/ops/themes.ops.d.ts +14 -0
  67. package/dist/platform/ops/themes.ops.js +47 -0
  68. package/dist/platform/platform-ops.module.d.ts +2 -1
  69. package/dist/platform/platform-ops.module.js +5 -0
  70. package/dist/platform/route-schemas.js +3 -0
  71. package/dist/runtime/subagents/definition.d.ts +3 -2
  72. package/dist/runtime/subagents/definition.js +2 -1
  73. package/dist/themes/render-css.d.ts +12 -0
  74. package/dist/themes/render-css.js +132 -0
  75. package/dist/themes/theme-delivery.service.d.ts +34 -0
  76. package/dist/themes/theme-delivery.service.js +111 -0
  77. package/dist/themes/theme-state.d.ts +95 -0
  78. package/dist/themes/theme-state.js +203 -0
  79. package/dist/themes/theme-usage.d.ts +61 -0
  80. package/dist/themes/theme-usage.js +98 -0
  81. package/dist/themes/themes-public.controller.d.ts +28 -0
  82. package/dist/themes/themes-public.controller.js +92 -0
  83. package/dist/themes/themes.controller.d.ts +13 -0
  84. package/dist/themes/themes.controller.js +38 -0
  85. package/dist/themes/themes.module.d.ts +3 -0
  86. package/dist/themes/themes.module.js +21 -0
  87. package/dist/themes/themes.schemas.d.ts +3 -0
  88. package/dist/themes/themes.schemas.js +2 -0
  89. package/dist/themes/themes.service.d.ts +50 -0
  90. package/dist/themes/themes.service.js +99 -0
  91. package/dist/tools/connector-account-policies.d.ts +51 -0
  92. package/dist/tools/connector-account-policies.js +165 -0
  93. package/dist/tools/connectors.controller.d.ts +35 -4
  94. package/dist/tools/connectors.controller.js +66 -7
  95. package/dist/tools/connectors.schemas.d.ts +11 -0
  96. package/dist/tools/connectors.schemas.js +11 -0
  97. package/dist/tools/connectors.service.d.ts +2 -0
  98. package/dist/tools/connectors.service.js +1 -0
  99. package/dist/tools/pipeline.d.ts +1 -1
  100. package/dist/tools/policy.d.ts +23 -1
  101. package/dist/tools/policy.js +99 -24
  102. package/dist/tools/tools.module.js +3 -0
  103. package/package.json +3 -3
  104. package/src/migrations/0102_themes.sql +64 -0
  105. package/src/migrations/0103_ui_theme_setting.sql +7 -0
  106. package/src/migrations/0104_connector_tool_account_policies.sql +16 -0
@@ -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
@@ -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";
@@ -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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.10",
3
+ "version": "0.1.0-rc.12",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,7 +65,7 @@
65
65
  "yaml": "^2.9.1",
66
66
  "yauzl": "3.4.0",
67
67
  "zod": "^4.6.7",
68
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.7"
68
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.8"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",
@@ -74,7 +74,7 @@
74
74
  "@types/ws": "^8.18.2",
75
75
  "@types/yauzl": "3.4.0",
76
76
  "ws": "^8.22.0",
77
- "@capybara-hq/testing": "0.1.0-rc.10"
77
+ "@capybara-hq/testing": "0.1.0-rc.11"
78
78
  },
79
79
  "scripts": {
80
80
  "build": "tsc -b"
@@ -0,0 +1,64 @@
1
+ -- Themes as a platform component kind (themes spec 2026-11-08-themes-design.md §6, §7): rows and
2
+ -- versions shaped like platform agent templates, with the owner columns of org-owned items; the
3
+ -- `theme` kind in the component scope, switch and install component tables; the person's pick on
4
+ -- the account. Expand-only: the only dropped objects are the three kind checks, re-added wider in
5
+ -- the next statement each.
6
+
7
+ CREATE TABLE IF NOT EXISTS platform_themes (
8
+ id text PRIMARY KEY,
9
+ catalog_item_id uuid REFERENCES catalog_items(id) ON DELETE SET NULL,
10
+ plugin_name text,
11
+ name text NOT NULL,
12
+ description text,
13
+ description_zh text,
14
+ current_version integer NOT NULL CHECK (current_version >= 1),
15
+ scope_mode text NOT NULL DEFAULT 'follow' CHECK (scope_mode IN ('follow', 'own')),
16
+ scope text NOT NULL DEFAULT 'optional' CHECK (scope IN ('all', 'optional', 'named')),
17
+ disabled boolean NOT NULL DEFAULT false,
18
+ shown_to_others boolean NOT NULL DEFAULT true,
19
+ scope_version integer NOT NULL DEFAULT 0,
20
+ -- The owning org (NULL = the platform), the org it was created in (no FK, immutable) and the
21
+ -- share switch, as on the other platform components (0084).
22
+ owner_org_id text REFERENCES orgs(id) ON DELETE SET NULL,
23
+ origin_org_id text,
24
+ shareable boolean NOT NULL DEFAULT false,
25
+ created_at timestamptz NOT NULL DEFAULT now(),
26
+ updated_at timestamptz NOT NULL DEFAULT now(),
27
+ deleted_at timestamptz
28
+ );
29
+ CREATE UNIQUE INDEX IF NOT EXISTS platform_themes_live_name_origin_uq
30
+ ON platform_themes ((coalesce(plugin_name, '')), name, (coalesce(origin_org_id, '')))
31
+ WHERE deleted_at IS NULL;
32
+
33
+ -- One version of a theme: `definition` is the parsed theme file (`ParsedTheme`); fonts and logo
34
+ -- live under `object_prefix` in `deployment/` (NULL for a theme without assets).
35
+ CREATE TABLE IF NOT EXISTS platform_theme_versions (
36
+ theme_id text NOT NULL REFERENCES platform_themes(id) ON DELETE CASCADE,
37
+ version integer NOT NULL CHECK (version >= 1),
38
+ definition jsonb NOT NULL,
39
+ object_prefix text,
40
+ catalog_version text,
41
+ updated_on_own boolean NOT NULL DEFAULT false,
42
+ created_by text,
43
+ created_at timestamptz NOT NULL DEFAULT now(),
44
+ PRIMARY KEY (theme_id, version)
45
+ );
46
+
47
+ -- 0081 declared these checks inline, so they carry the default names.
48
+ -- capybara:allow-destructive the check is added back with 'theme' in the next statement; existing rows satisfy it
49
+ ALTER TABLE component_scope_orgs DROP CONSTRAINT IF EXISTS component_scope_orgs_kind_check;
50
+ ALTER TABLE component_scope_orgs ADD CONSTRAINT component_scope_orgs_kind_check
51
+ CHECK (kind IN ('skill', 'agent_template', 'connector_template', 'theme'));
52
+ -- capybara:allow-destructive the check is added back with 'theme' in the next statement; existing rows satisfy it
53
+ ALTER TABLE component_org_switches DROP CONSTRAINT IF EXISTS component_org_switches_kind_check;
54
+ ALTER TABLE component_org_switches ADD CONSTRAINT component_org_switches_kind_check
55
+ CHECK (kind IN ('skill', 'agent_template', 'connector_template', 'theme'));
56
+ -- NOT VALID as in 0053: existing rows were checked by the narrower constraint already.
57
+ -- capybara:allow-destructive the check is added back with 'theme' in the next statement; existing rows satisfy it
58
+ ALTER TABLE plugin_install_components DROP CONSTRAINT IF EXISTS plugin_install_components_kind_check;
59
+ ALTER TABLE plugin_install_components ADD CONSTRAINT plugin_install_components_kind_check
60
+ CHECK (kind IN ('extension', 'connector', 'skill', 'plugin', 'agent_template', 'theme')) NOT VALID;
61
+
62
+ -- The person's theme (§7): one value across all their orgs; NULL follows the org. No FK: a pick
63
+ -- that is not on in the current org (or no longer exists) is skipped, never an error.
64
+ ALTER TABLE accounts ADD COLUMN IF NOT EXISTS theme text;
@@ -0,0 +1,7 @@
1
+ -- The platform default theme setting `uiTheme` (themes spec §7) always has a row: the guards that
2
+ -- refuse taking the default theme away from an org read it FOR SHARE, and the setting's write
3
+ -- locks it FOR UPDATE before checking the chosen theme, so the two serialize on this row.
4
+ -- `{"id": null}` is the built-in theme (the default when the key is missing).
5
+ INSERT INTO deployment_settings (key, value)
6
+ VALUES ('uiTheme', '{"id": null}'::jsonb)
7
+ ON CONFLICT (key) DO NOTHING;
@@ -0,0 +1,16 @@
1
+ -- A member's own tool policies for their own connection of a personal-credential connector
2
+ -- (maintainer decision 2026-11-09, the connector card's 权限): one row per account, connector and
3
+ -- tool name. They apply to every agent the account uses; an agent's tool policy overrides them, and
4
+ -- a tool the org switched off (or set to deny) stays denied whatever they say.
5
+ CREATE TABLE IF NOT EXISTS capybara.connector_tool_account_policies (
6
+ org_id text NOT NULL REFERENCES capybara.orgs(id) ON DELETE CASCADE,
7
+ connector_id text NOT NULL REFERENCES capybara.connectors(id) ON DELETE CASCADE,
8
+ account_id text NOT NULL REFERENCES capybara.accounts(id) ON DELETE CASCADE,
9
+ tool_name text NOT NULL,
10
+ policy text NOT NULL CHECK (policy IN ('allow', 'ask', 'deny')),
11
+ updated_at timestamptz NOT NULL DEFAULT now(),
12
+ PRIMARY KEY (connector_id, account_id, tool_name)
13
+ );
14
+
15
+ CREATE INDEX IF NOT EXISTS connector_tool_account_policies_account
16
+ ON capybara.connector_tool_account_policies (account_id);