Q104: connector grants can narrow a toolkit to named actions and fixed argument values
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b02834893f
commit
558d678ba2
14 files changed
+202
-26
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { requireString } from "./agent-tool-args";
|
||||
import { optionalStringArray, requireString } from "./agent-tool-args";
|
||||
import type { AgentToolContext, AgentToolDeps } from "./agent-tools";
|
||||
import { ConnectorGrantsService } from "./composio/connector-grants";
|
||||
import { ConnectorGrantsService, type ConnectorGrantLimits } from "./composio/connector-grants";
|
||||
import type { JsonRecord } from "./provider-http";
|
||||
|
||||
/** Company connector visibility for any agent: what is connected, who holds it. */
|
||||
@@ -18,8 +18,7 @@ export function listConnectorsTool(deps: AgentToolDeps, ctx: AgentToolContext):
|
||||
const lines = accounts.map((account) => {
|
||||
const holders = grants
|
||||
.filter((grant) => grant.toolkitSlug === account.toolkitSlug && grant.accountLabel === account.label)
|
||||
.map((grant) => agentNames.get(grant.agentId))
|
||||
.filter(Boolean);
|
||||
.map((grant) => `${agentNames.get(grant.agentId)}${limitNote(grant.limits)}`);
|
||||
return `- ${account.toolkitName} (${account.toolkitSlug}), account "${account.label}": ${holders.length > 0 ? `granted to ${holders.join(", ")}` : "no grants yet"}`;
|
||||
});
|
||||
return ["Connected toolkits:", ...lines].join("\n");
|
||||
@@ -45,11 +44,38 @@ export function grantConnectorTool(deps: AgentToolDeps, ctx: AgentToolContext, a
|
||||
agentId: target.id,
|
||||
toolkitSlug: toolkit,
|
||||
accountLabel: args.account === undefined ? null : requireString(args.account, "account"),
|
||||
limits: {
|
||||
actions: args.actions === undefined ? null : optionalStringArray(args.actions, "actions"),
|
||||
fixedArguments: args.fixed_arguments === undefined ? null : requireObject(args.fixed_arguments, "fixed_arguments"),
|
||||
},
|
||||
grantedByType: "agent",
|
||||
grantedById: ctx.agent.id,
|
||||
});
|
||||
return `${target.name} now has access to the ${toolkit.toLowerCase()} connector through its "${label}" account; its tools join their next run.`;
|
||||
return `${target.name} now has access to the ${toolkit.toLowerCase()} connector through its "${label}" account${limitText(args)}; its tools join their next run.`;
|
||||
}
|
||||
grantsService.revoke({ orgId: ctx.orgId, agentId: target.id, toolkitSlug: toolkit });
|
||||
return `${target.name} no longer has access to the ${toolkit.toLowerCase()} connector.`;
|
||||
}
|
||||
|
||||
function requireObject(value: unknown, field: string): Record<string, unknown> {
|
||||
if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).length === 0) {
|
||||
throw new Error(`"${field}" must be an object of argument values.`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function limitText(args: JsonRecord): string {
|
||||
return limitNote({
|
||||
actions: Array.isArray(args.actions) ? args.actions.map((action) => String(action).trim().toUpperCase()) : null,
|
||||
fixedArguments: args.fixed_arguments === undefined ? null : args.fixed_arguments as Record<string, unknown>,
|
||||
});
|
||||
}
|
||||
|
||||
/** A grant's limits in a few words, empty when it has none. */
|
||||
function limitNote(limits: ConnectorGrantLimits): string {
|
||||
const parts = [
|
||||
...(limits.actions ? [`only ${limits.actions.join(", ")}`] : []),
|
||||
...(limits.fixedArguments ? [`limited to ${JSON.stringify(limits.fixedArguments)}`] : []),
|
||||
];
|
||||
return parts.length > 0 ? ` (${parts.join("; ")})` : "";
|
||||
}
|
||||
Reference in new issue
Block a user