Skip to content

Commit 1e5b808

Browse files
[codex] Add server-side Composio managed auth (#735)
Co-authored-by: James Grugett <jahooma@gmail.com>
1 parent 117be5a commit 1e5b808

27 files changed

Lines changed: 5338 additions & 35 deletions

File tree

agents/base2/base2.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { buildArray } from '@codebuff/common/util/array'
2+
import { COMPOSIO_META_TOOL_NAMES } from '@codebuff/common/constants/composio'
23
import {
34
FREEBUFF_GEMINI_THINKER_AGENT_ID,
45
FREEBUFF_GEMINI_THINKER_INSTRUCTIONS_PROMPT,
@@ -17,6 +18,8 @@ import {
1718
type SecretAgentDefinition,
1819
} from '../types/secret-agent-definition'
1920

21+
const ENABLE_COMPOSIO_TOOLS = false
22+
2023
export function createBase2(
2124
mode: 'default' | 'free' | 'lite' | 'max' | 'fast',
2225
options?: {
@@ -105,6 +108,7 @@ export function createBase2(
105108
'set_output',
106109
'list_directory',
107110
'glob',
111+
ENABLE_COMPOSIO_TOOLS && COMPOSIO_META_TOOL_NAMES,
108112
),
109113
spawnableAgents: buildArray(
110114
!isMax && 'file-picker',
@@ -148,7 +152,8 @@ Current date: ${PLACEHOLDER.CURRENT_DATE}.
148152
}
149153
- **Be careful about terminal commands:** Be careful about instructing subagents to run terminal commands that could be destructive or have effects that are hard to undo (e.g. git push, git commit, running any scripts -- especially ones that could alter production environments (!), installing packages globally, etc). Don't run any of these effectful commands unless the user explicitly asks you to.
150154
- **Do what the user asks:** If the user asks you to do something, even running a risky terminal command, do it.
151-
- **Don't use set_output:** The set_output tool is for spawned subagents to report results. Don't use it yourself.
155+
- **Don't use set_output:** The set_output tool is for spawned subagents to report results. Don't use it yourself.${ENABLE_COMPOSIO_TOOLS ? `
156+
- **External apps:** When Composio tools are available and the user asks to work with connected apps or services like Gmail, Google Calendar, GitHub, Slack, Linear, or Notion, use them to search for the right app tools, help the user connect their account, and execute the requested action.` : ''}
152157
153158
# Code Editing Mandates
154159

agents/types/secret-agent-definition.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AgentDefinition } from './agent-definition'
22
import type * as Tools from './tools'
3+
import type { ComposioMetaToolName } from '@codebuff/common/constants/composio'
34
export type { Tools }
45

56
export type AllToolNames =
@@ -9,9 +10,12 @@ export type AllToolNames =
910
| 'create_plan'
1011
| 'spawn_agent_inline'
1112
| 'update_subgoal'
13+
| ComposioMetaToolName
1214

13-
export interface SecretAgentDefinition
14-
extends Omit<AgentDefinition, 'toolNames'> {
15+
export interface SecretAgentDefinition extends Omit<
16+
AgentDefinition,
17+
'toolNames'
18+
> {
1519
/** Tools this agent can use. */
1620
toolNames?: AllToolNames[]
1721
}

bun.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/src/constants/composio.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export const COMPOSIO_API_KEY_ENV_VAR = 'COMPOSIO_API_KEY'
2+
3+
export const COMPOSIO_META_TOOL_NAMES = [
4+
'composio_manage_connections',
5+
'composio_multi_execute_tool',
6+
'composio_search_tools',
7+
'composio_get_tool_schemas',
8+
] as const
9+
10+
export type ComposioMetaToolName = (typeof COMPOSIO_META_TOOL_NAMES)[number]
11+
12+
export const COMPOSIO_META_TOOL_NAME_TO_UPSTREAM = {
13+
composio_manage_connections: 'COMPOSIO_MANAGE_CONNECTIONS',
14+
composio_multi_execute_tool: 'COMPOSIO_MULTI_EXECUTE_TOOL',
15+
composio_search_tools: 'COMPOSIO_SEARCH_TOOLS',
16+
composio_get_tool_schemas: 'COMPOSIO_GET_TOOL_SCHEMAS',
17+
} as const satisfies Record<ComposioMetaToolName, string>
18+
19+
export type ComposioUpstreamMetaToolName =
20+
(typeof COMPOSIO_META_TOOL_NAME_TO_UPSTREAM)[ComposioMetaToolName]
21+
22+
const COMPOSIO_META_TOOL_NAME_SET = new Set<string>(COMPOSIO_META_TOOL_NAMES)
23+
24+
export function isComposioMetaToolName(
25+
toolName: string,
26+
): toolName is ComposioMetaToolName {
27+
return COMPOSIO_META_TOOL_NAME_SET.has(toolName)
28+
}
29+
30+
export function getComposioUpstreamToolName(
31+
toolName: ComposioMetaToolName,
32+
): ComposioUpstreamMetaToolName {
33+
return COMPOSIO_META_TOOL_NAME_TO_UPSTREAM[toolName]
34+
}

common/src/tools/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { COMPOSIO_META_TOOL_NAMES } from '../constants/composio'
2+
13
import type { ToolResultOutput } from '../types/messages/content-part'
24
import type { Tool } from 'ai'
35

@@ -56,6 +58,7 @@ export const toolNames = [
5658
'web_search',
5759
'write_file',
5860
'write_todos',
61+
...COMPOSIO_META_TOOL_NAMES,
5962
] as const
6063

6164
export const publishedTools = [

common/src/tools/list.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { applyPatchParams } from './params/tool/apply-patch'
77
import { askUserParams } from './params/tool/ask-user'
88
import { browserLogsParams } from './params/tool/browser-logs'
99
import { codeSearchParams } from './params/tool/code-search'
10+
import { composioMetaToolParams } from './params/tool/composio'
1011
import { createPlanParams } from './params/tool/create-plan'
1112
import { endTurnParams } from './params/tool/end-turn'
1213
import { findFilesParams } from './params/tool/find-files'
@@ -77,6 +78,7 @@ export const toolParams = {
7778
web_search: webSearchParams,
7879
write_file: writeFileParams,
7980
write_todos: writeTodosParams,
81+
...composioMetaToolParams,
8082
} satisfies {
8183
[K in ToolName]: $ToolParams<K>
8284
}
@@ -151,6 +153,22 @@ export const clientToolCallSchema = z.discriminatedUnion('toolName', [
151153
toolName: z.literal('write_file'),
152154
input: FileChangeSchema,
153155
}),
156+
z.object({
157+
toolName: z.literal('composio_manage_connections'),
158+
input: toolParams.composio_manage_connections.inputSchema,
159+
}),
160+
z.object({
161+
toolName: z.literal('composio_multi_execute_tool'),
162+
input: toolParams.composio_multi_execute_tool.inputSchema,
163+
}),
164+
z.object({
165+
toolName: z.literal('composio_search_tools'),
166+
input: toolParams.composio_search_tools.inputSchema,
167+
}),
168+
z.object({
169+
toolName: z.literal('composio_get_tool_schemas'),
170+
input: toolParams.composio_get_tool_schemas.inputSchema,
171+
}),
154172
])
155173
export const clientToolNames = clientToolCallSchema.def.options.map(
156174
(opt) => opt.shape.toolName.value,
@@ -163,4 +181,4 @@ export type ClientToolCall<T extends ClientToolName = ClientToolName> = Extract<
163181
> &
164182
Pick<ToolCallPart, 'toolCallId' | 'toolName' | 'input' | 'providerOptions'>
165183

166-
export type PublishedClientToolName = ClientToolName & PublishedToolName
184+
export type PublishedClientToolName = Extract<ClientToolName, PublishedToolName>
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { COMPOSIO_META_TOOL_NAMES } from '../../../constants/composio'
2+
import z from 'zod/v4'
3+
4+
import { jsonToolResultSchema } from '../utils'
5+
6+
import type { $ToolParams } from '../../constants'
7+
8+
const sessionIdParam = z
9+
.string()
10+
.optional()
11+
.describe('Session ID returned by composio_search_tools, when available.')
12+
13+
const composioMetaToolInputSchemas = {
14+
composio_search_tools: z
15+
.object({
16+
queries: z
17+
.array(z.unknown())
18+
.min(1)
19+
.describe(
20+
'Structured English search queries. Split independent app/API actions into separate queries.',
21+
),
22+
session: z
23+
.object({
24+
generate_id: z.boolean().optional(),
25+
id: z.string().optional(),
26+
})
27+
.catchall(z.unknown())
28+
.describe(
29+
'Use { generate_id: true } for a new workflow, or { id } to continue one.',
30+
),
31+
model: z.string().optional().describe('Client LLM model name.'),
32+
})
33+
.catchall(z.unknown()),
34+
composio_get_tool_schemas: z
35+
.object({
36+
tool_slugs: z
37+
.array(z.string())
38+
.min(1)
39+
.describe('Composio tool slugs to retrieve schemas for.'),
40+
include: z
41+
.array(z.string())
42+
.optional()
43+
.describe('Schema fields to include, e.g. input_schema/output_schema.'),
44+
session_id: sessionIdParam,
45+
})
46+
.catchall(z.unknown()),
47+
composio_manage_connections: z
48+
.object({
49+
toolkits: z
50+
.array(z.string())
51+
.min(1)
52+
.describe('Toolkit slugs to check or connect, such as gmail/github.'),
53+
reinitiate_all: z
54+
.boolean()
55+
.optional()
56+
.describe('Force reconnection even if active credentials exist.'),
57+
session_id: sessionIdParam,
58+
})
59+
.catchall(z.unknown()),
60+
composio_multi_execute_tool: z
61+
.object({
62+
tools: z
63+
.array(z.record(z.string(), z.unknown()))
64+
.min(1)
65+
.describe('Logically independent Composio tools to execute.'),
66+
thought: z
67+
.string()
68+
.optional()
69+
.describe('One concise sentence explaining the execution intent.'),
70+
sync_response_to_workbench: z
71+
.boolean()
72+
.default(false)
73+
.describe('Always use false. Codebuff disables Composio workbench.'),
74+
session_id: sessionIdParam,
75+
})
76+
.catchall(z.unknown()),
77+
}
78+
79+
const composioMetaToolDescriptions = {
80+
composio_search_tools:
81+
'Discover relevant Composio tools across external apps. Use this first for requests involving services like Gmail, GitHub, Slack, Linear, Notion, Google Calendar, or Google Sheets.',
82+
composio_get_tool_schemas:
83+
'Retrieve complete input schemas for specific Composio tool slugs returned by composio_search_tools.',
84+
composio_manage_connections:
85+
'Check or initiate user authentication for external app toolkits. Use when search/execution indicates a toolkit is not connected.',
86+
composio_multi_execute_tool:
87+
'Execute one or more discovered Composio app tools in the current workflow session. Do not use workbench offloading.',
88+
}
89+
90+
const composioOutputSchema = jsonToolResultSchema(
91+
z.union([
92+
z.json(),
93+
z.object({
94+
errorMessage: z.string(),
95+
status: z.number().optional(),
96+
}),
97+
]),
98+
)
99+
100+
export const composioMetaToolParams = {
101+
composio_manage_connections: {
102+
toolName: 'composio_manage_connections',
103+
endsAgentStep: true,
104+
description: composioMetaToolDescriptions.composio_manage_connections,
105+
inputSchema: composioMetaToolInputSchemas.composio_manage_connections,
106+
outputSchema: composioOutputSchema,
107+
},
108+
composio_multi_execute_tool: {
109+
toolName: 'composio_multi_execute_tool',
110+
endsAgentStep: true,
111+
description: composioMetaToolDescriptions.composio_multi_execute_tool,
112+
inputSchema: composioMetaToolInputSchemas.composio_multi_execute_tool,
113+
outputSchema: composioOutputSchema,
114+
},
115+
composio_search_tools: {
116+
toolName: 'composio_search_tools',
117+
endsAgentStep: true,
118+
description: composioMetaToolDescriptions.composio_search_tools,
119+
inputSchema: composioMetaToolInputSchemas.composio_search_tools,
120+
outputSchema: composioOutputSchema,
121+
},
122+
composio_get_tool_schemas: {
123+
toolName: 'composio_get_tool_schemas',
124+
endsAgentStep: true,
125+
description: composioMetaToolDescriptions.composio_get_tool_schemas,
126+
inputSchema: composioMetaToolInputSchemas.composio_get_tool_schemas,
127+
outputSchema: composioOutputSchema,
128+
},
129+
} satisfies {
130+
[K in (typeof COMPOSIO_META_TOOL_NAMES)[number]]: $ToolParams<K>
131+
}

packages/agent-runtime/src/tools/handlers/list.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import { handleApplyPatch } from './tool/apply-patch'
44
import { handleAskUser } from './tool/ask-user'
55
import { handleBrowserLogs } from './tool/browser-logs'
66
import { handleCodeSearch } from './tool/code-search'
7+
import {
8+
handleComposioGetToolSchemas,
9+
handleComposioManageConnections,
10+
handleComposioMultiExecute,
11+
handleComposioSearchTools,
12+
} from './tool/composio'
713
import { handleCreatePlan } from './tool/create-plan'
814
import { handleEndTurn } from './tool/end-turn'
915
import { handleFindFiles } from './tool/find-files'
@@ -46,13 +52,19 @@ import type { ToolName } from '@codebuff/common/tools/constants'
4652
* - Any additional arguments for the tool
4753
* - Returns a promise that will be awaited
4854
*/
49-
export const codebuffToolHandlers = {
55+
export const codebuffToolHandlers: {
56+
[K in ToolName]: CodebuffToolHandlerFunction<K>
57+
} = {
5058
add_message: handleAddMessage,
5159
add_subgoal: handleAddSubgoal,
5260
apply_patch: handleApplyPatch,
5361
ask_user: handleAskUser,
5462
browser_logs: handleBrowserLogs,
5563
code_search: handleCodeSearch,
64+
composio_manage_connections: handleComposioManageConnections,
65+
composio_multi_execute_tool: handleComposioMultiExecute,
66+
composio_search_tools: handleComposioSearchTools,
67+
composio_get_tool_schemas: handleComposioGetToolSchemas,
5668
create_plan: handleCreatePlan,
5769
end_turn: handleEndTurn,
5870
find_files: handleFindFiles,
@@ -82,6 +94,4 @@ export const codebuffToolHandlers = {
8294
web_search: handleWebSearch,
8395
write_file: handleWriteFile,
8496
write_todos: handleWriteTodos,
85-
} satisfies {
86-
[K in ToolName]: CodebuffToolHandlerFunction<K>
8797
}

0 commit comments

Comments
 (0)