@capybara-hq/kernel 0.1.0-rc.11 → 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.
- package/dist/agents/agents.schemas.d.ts +3 -0
- package/dist/agents/agents.service.js +11 -1
- package/dist/agents/definition.d.ts +1 -0
- package/dist/agents/definition.js +7 -3
- package/dist/agents/org-templates.js +4 -0
- package/dist/agents/pending-connectors.service.js +1 -1
- package/dist/catalog/component-scope.controller.d.ts +18 -6
- package/dist/catalog/component-scope.controller.js +36 -13
- package/dist/catalog/component-scope.service.d.ts +11 -0
- package/dist/catalog/component-scope.service.js +4 -0
- package/dist/catalog/component-state.d.ts +8 -0
- package/dist/catalog/component-state.js +19 -11
- package/dist/catalog/plugin-import.js +4 -1
- package/dist/conversations/conversations.service.d.ts +9 -0
- package/dist/conversations/conversations.service.js +9 -2
- package/dist/conversations/events.d.ts +1 -0
- package/dist/db/kernel-schema.d.ts +13 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/platform/exclusions.js +10 -0
- package/dist/platform/ops/agents.ops.js +19 -6
- package/dist/runtime/subagents/definition.d.ts +3 -2
- package/dist/runtime/subagents/definition.js +2 -1
- package/dist/tools/connector-account-policies.d.ts +51 -0
- package/dist/tools/connector-account-policies.js +165 -0
- package/dist/tools/connectors.controller.d.ts +35 -4
- package/dist/tools/connectors.controller.js +66 -7
- package/dist/tools/connectors.schemas.d.ts +11 -0
- package/dist/tools/connectors.schemas.js +11 -0
- package/dist/tools/connectors.service.d.ts +2 -0
- package/dist/tools/connectors.service.js +1 -0
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/policy.d.ts +23 -1
- package/dist/tools/policy.js +99 -24
- package/dist/tools/tools.module.js +3 -0
- package/package.json +1 -1
- package/src/migrations/0104_connector_tool_account_policies.sql +16 -0
package/dist/tools/policy.js
CHANGED
|
@@ -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
|
|
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
|
|
41
|
-
* (`Platform:<operation>`), on that key, the stricter of the two
|
|
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
|
|
44
|
-
const own =
|
|
54
|
+
function settingOf(policies, toolKey, policyKey) {
|
|
55
|
+
const own = asSetting(policies?.[toolKey]);
|
|
45
56
|
if (policyKey === toolKey)
|
|
46
57
|
return own;
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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
|
|
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
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
:
|
|
190
|
-
? "
|
|
191
|
-
:
|
|
192
|
-
? "
|
|
193
|
-
:
|
|
194
|
-
? "
|
|
195
|
-
:
|
|
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
|
|
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
|
|
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
|
@@ -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);
|