Brief agents on their connectors, skills, and the CEO's grant authority; CEO predicate follows role_level

This commit is contained in:
ZacharyZhang-NY committed 2026-07-07 14:19:08 -04:00
1 parent 3e4c7c81a2
commit a95859a9f7
3 files changed
+171 -4

No files matched your search

+9 -2
View File
@@ -24,6 +24,8 @@ import {
type WorkspaceRow,
} from "./task-prompts";
import { loadComposioTools } from "./composio/composio-tool-bridge";
import { connectorDisplayName } from "./composio/connector-brands";
import { ConnectorGrantsService } from "./composio/connector-grants";
const TASK_RUN_STATE = {
QUEUED: "queued",
@@ -122,8 +124,13 @@ export class TaskExecutionService {
};
const chatContext = this.loadChatContext(orgId, isolation);
const inboxContext = chatContext ? null : this.loadInboxContext(orgId, agent.id);
const isCeo = !agent.manager_agent_id;
// The CEO is the c_suite role — the same predicate the grant system
// enforces — not "has no manager", which any board hire can match.
const isCeo = agent.role_level === "c_suite";
const hasReports = this.agentHasReports(orgId, agent.id);
const connectorNames = new ConnectorGrantsService(this.db)
.toolkitsForAgent(orgId, agent.id)
.map(connectorDisplayName);
const externalTools = await loadComposioTools(this.db, orgId, agent.id);
const result = await runAgentLoop(
{
@@ -135,7 +142,7 @@ export class TaskExecutionService {
orgId,
providerId: provider.id,
workspaceId: task.workspace_id,
instructions: buildAgentInstructions({ agent, companyName: this.orgName(orgId), isCeo, hasReports, externalToolCount: externalTools.length }),
instructions: buildAgentInstructions({ agent, companyName: this.orgName(orgId), isCeo, hasReports, connectorNames }),
initialUserTurn: buildTaskPrompt({
task,
agent,
+35 -2
View File
@@ -76,7 +76,8 @@ export interface AgentInstructionContext {
companyName: string;
isCeo: boolean;
hasReports: boolean;
externalToolCount: number;
/** Display names of the connected toolkits this agent carries in this run. */
connectorNames: string[];
}
/**
@@ -91,11 +92,43 @@ export function buildAgentInstructions(input: AgentInstructionContext): string {
`You are ${agent.name}, ${agent.title || agent.role} in ${agent.department} at ${input.companyName}. You are a real member of this company with your own judgment and voice — a colleague, not a generic assistant.`,
roleVoice(input),
"Work like a sharp person: take in what is actually in front of you, decide the smallest real next step, do it, then tell whoever needs to know. Do not narrate busywork.",
`Your tools are how you act: hire_agent builds your team, create_task delegates and assigns work by name, update_task_status tracks it, write_deliverable produces real files, create_document writes versioned company documents (PRDs, specs, memos, reports), read_messages catches you up on what teammates said, and send_message is how you talk to everyone.${input.externalToolCount > 0 ? ` You also have ${input.externalToolCount} external tools (composio_* prefixed) for interacting with services like GitHub, Slack, Gmail, etc. — use them when your work involves those platforms.` : ""}`,
`Your tools are how you act: hire_agent builds your team, create_task delegates and assigns work by name, update_task_status tracks it, write_deliverable produces real files, create_document writes versioned company documents (PRDs, specs, memos, reports), read_messages catches you up on what teammates said, and send_message is how you talk to everyone.`,
capabilityBriefing(input),
"Talk like a person on a team, not a status bot: address teammates by @name, say what you genuinely think, ask when you are unsure, and reply when someone asks you something. Before starting work, check your messages with read_messages to see if anyone needs you. Post where the work lives — 'engineering', 'product', or otherwise 'general' — and keep a back-and-forth in one thread by passing the thread_id a previous send_message returned. Hand work to a teammate with /handoff @Name and the file_id write_deliverable gives you.",
].join("\n");
}
/**
* What this agent can reach beyond the built-in tools, and how access moves:
* the CEO carries everything and hands out grants; everyone else works with
* what they were granted and knows where to ask for more.
*/
function capabilityBriefing(input: AgentInstructionContext): string {
const lines: string[] = [];
if (input.connectorNames.length > 0) {
lines.push(
`Connected external services you carry in this run: ${input.connectorNames.join(", ")}. Their tools arrive with the composio_ prefix — use them directly whenever the work touches those platforms instead of describing what you would do.`,
);
}
lines.push(
"list_connectors shows every service the company has connected and who holds it; list_skills shows the company's skill library. Check them before planning work that depends on an external platform or a playbook.",
);
if (input.isCeo) {
lines.push(
"You are the CEO: you automatically carry every connected service and every company skill. Access for everyone else is yours to manage — when you delegate work that needs an external service or a skill, grant it to that teammate first with grant_connector or grant_skill (match the tool to the job), and take it back with revoke_connector or revoke_skill when the work is done. A teammate without the grant cannot use the tool, so grant before you assign.",
);
} else if (input.connectorNames.length === 0) {
lines.push(
"You currently hold no connector grants. If this work needs an external service, message the CEO to grant it to you (they run grant_connector) rather than working around it.",
);
} else {
lines.push(
"If the work needs a service you do not carry yet, message the CEO to grant it to you (they run grant_connector).",
);
}
return lines.join(" ");
}
function roleVoice(input: AgentInstructionContext): string {
if (input.isCeo) {
return "You run this company. Think in outcomes, make the call when it is yours to make, and put the right person on each piece instead of doing everything yourself — then tell the team the why, not just the what.";