Skip to content

Commit fdf4f03

Browse files
committed
Fixes
1 parent 5a86b4e commit fdf4f03

File tree

8 files changed

+72
-175
lines changed

8 files changed

+72
-175
lines changed

apps/sim/lib/copilot/orchestrator/tool-executor/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,6 @@ const SERVER_TOOLS = new Set<string>([
702702
'make_api_request',
703703
'knowledge_base',
704704
'user_table',
705-
'run_workflow',
706-
'run_workflow_until_block',
707-
'run_block',
708-
'run_from_block',
709705
'workspace_file',
710706
'get_execution_summary',
711707
'get_job_logs',

apps/sim/lib/copilot/orchestrator/tool-executor/integration-tools.ts

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ export async function executeIntegrationToolDirect(
5252
// has active credential_member access before proceeding. This prevents
5353
// unauthorized credential usage even if the agent hallucinated or received
5454
// a credential ID the user doesn't have access to.
55-
const suppliedCredentialId = (executionParams.oauthCredential || executionParams.credential) as
56-
| string
57-
| undefined
55+
const suppliedCredentialId = (executionParams.credentialId ||
56+
executionParams.oauthCredential ||
57+
executionParams.credential) as string | undefined
5858
if (suppliedCredentialId) {
5959
const actorCtx = await getCredentialActorContext(suppliedCredentialId, userId)
6060
if (!actorCtx.member) {
@@ -73,9 +73,10 @@ export async function executeIntegrationToolDirect(
7373
if (toolConfig.oauth?.required && toolConfig.oauth.provider) {
7474
const provider = toolConfig.oauth.provider
7575

76-
// If the user already supplied a credential ID that passed the check above,
77-
// skip auto-resolution and let executeTool handle it via the token endpoint.
78-
if (!suppliedCredentialId) {
76+
// Determine which credential to use: supplied by the LLM or auto-resolved
77+
let resolvedCredentialId = suppliedCredentialId
78+
79+
if (!resolvedCredentialId) {
7980
if (!workspaceId) {
8081
return {
8182
success: false,
@@ -93,37 +94,38 @@ export async function executeIntegrationToolDirect(
9394
}
9495
}
9596

96-
// Resolve the credential to its underlying account for token refresh
97-
const matchCtx = await getCredentialActorContext(match.id, userId)
98-
const accountId = matchCtx.credential?.accountId
99-
if (!accountId) {
100-
return {
101-
success: false,
102-
error: `OAuth account for ${provider} not found. Please reconnect your account.`,
103-
}
97+
resolvedCredentialId = match.id
98+
}
99+
100+
const matchCtx = await getCredentialActorContext(resolvedCredentialId, userId)
101+
const accountId = matchCtx.credential?.accountId
102+
if (!accountId) {
103+
return {
104+
success: false,
105+
error: `OAuth account for ${provider} not found. Please reconnect your account.`,
104106
}
107+
}
105108

106-
const [acc] = await db.select().from(account).where(eq(account.id, accountId)).limit(1)
109+
const [acc] = await db.select().from(account).where(eq(account.id, accountId)).limit(1)
107110

108-
if (!acc) {
109-
return {
110-
success: false,
111-
error: `OAuth account for ${provider} not found. Please reconnect your account.`,
112-
}
111+
if (!acc) {
112+
return {
113+
success: false,
114+
error: `OAuth account for ${provider} not found. Please reconnect your account.`,
113115
}
116+
}
114117

115-
const requestId = generateRequestId()
116-
const { accessToken } = await refreshTokenIfNeeded(requestId, acc, acc.id)
118+
const requestId = generateRequestId()
119+
const { accessToken } = await refreshTokenIfNeeded(requestId, acc, acc.id)
117120

118-
if (!accessToken) {
119-
return {
120-
success: false,
121-
error: `OAuth token not available for ${provider}. Please reconnect your account.`,
122-
}
121+
if (!accessToken) {
122+
return {
123+
success: false,
124+
error: `OAuth token not available for ${provider}. Please reconnect your account.`,
123125
}
124-
125-
executionParams.accessToken = accessToken
126126
}
127+
128+
executionParams.accessToken = accessToken
127129
}
128130

129131
const hasHostedKeySupport = isHosted && !!toolConfig.hosting

apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/mutations.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ export async function executeCreateWorkflow(
105105
return { success: false, error: 'Description must be 2000 characters or less' }
106106
}
107107

108-
const workspaceId = params?.workspaceId || (await getDefaultWorkspaceId(context.userId))
108+
const workspaceId =
109+
params?.workspaceId || context.workspaceId || (await getDefaultWorkspaceId(context.userId))
109110
const folderId = params?.folderId || null
110111

111112
await ensureWorkspaceAccess(workspaceId, context.userId, true)
@@ -145,7 +146,8 @@ export async function executeCreateFolder(
145146
return { success: false, error: 'Folder name must be 200 characters or less' }
146147
}
147148

148-
const workspaceId = params?.workspaceId || (await getDefaultWorkspaceId(context.userId))
149+
const workspaceId =
150+
params?.workspaceId || context.workspaceId || (await getDefaultWorkspaceId(context.userId))
149151
const parentId = params?.parentId || null
150152

151153
await ensureWorkspaceAccess(workspaceId, context.userId, true)
@@ -426,7 +428,8 @@ export async function executeGenerateApiKey(
426428
return { success: false, error: 'API key name must be 200 characters or less' }
427429
}
428430

429-
const workspaceId = params.workspaceId || (await getDefaultWorkspaceId(context.userId))
431+
const workspaceId =
432+
params.workspaceId || context.workspaceId || (await getDefaultWorkspaceId(context.userId))
430433
await ensureWorkspaceAccess(workspaceId, context.userId, true)
431434

432435
const newKey = await createWorkspaceApiKey({

apps/sim/lib/copilot/orchestrator/tool-executor/workflow-tools/queries.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ export async function executeListFolders(
4242
): Promise<ToolCallResult> {
4343
try {
4444
const workspaceId =
45-
(params?.workspaceId as string | undefined) || (await getDefaultWorkspaceId(context.userId))
45+
(params?.workspaceId as string | undefined) ||
46+
context.workspaceId ||
47+
(await getDefaultWorkspaceId(context.userId))
4648

4749
await ensureWorkspaceAccess(workspaceId, context.userId, false)
4850

apps/sim/lib/copilot/tools/mcp/definitions.ts

Lines changed: 0 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -171,130 +171,6 @@ export const DIRECT_TOOL_DEFS: DirectToolDef[] = [
171171
},
172172
annotations: { destructiveHint: false, idempotentHint: true },
173173
},
174-
{
175-
name: 'run_workflow',
176-
toolId: 'run_workflow',
177-
description:
178-
'Run a workflow and return its output. Works on both draft and deployed states. By default runs the draft (live) state.',
179-
inputSchema: {
180-
type: 'object',
181-
properties: {
182-
workflowId: {
183-
type: 'string',
184-
description: 'REQUIRED. The workflow ID to run.',
185-
},
186-
workflow_input: {
187-
type: 'object',
188-
description:
189-
'JSON object with input values. Keys should match the workflow start block input field names.',
190-
},
191-
useDeployedState: {
192-
type: 'boolean',
193-
description: 'When true, runs the deployed version instead of the draft. Default: false.',
194-
},
195-
},
196-
required: ['workflowId'],
197-
},
198-
annotations: { destructiveHint: false, openWorldHint: true },
199-
},
200-
{
201-
name: 'run_workflow_until_block',
202-
toolId: 'run_workflow_until_block',
203-
description:
204-
'Run a workflow and stop after a specific block completes. Useful for testing partial execution or debugging specific blocks.',
205-
inputSchema: {
206-
type: 'object',
207-
properties: {
208-
workflowId: {
209-
type: 'string',
210-
description: 'REQUIRED. The workflow ID to run.',
211-
},
212-
stopAfterBlockId: {
213-
type: 'string',
214-
description:
215-
'REQUIRED. The block ID to stop after. Execution halts once this block completes.',
216-
},
217-
workflow_input: {
218-
type: 'object',
219-
description: 'JSON object with input values for the workflow.',
220-
},
221-
useDeployedState: {
222-
type: 'boolean',
223-
description: 'When true, runs the deployed version instead of the draft. Default: false.',
224-
},
225-
},
226-
required: ['workflowId', 'stopAfterBlockId'],
227-
},
228-
annotations: { destructiveHint: false, openWorldHint: true },
229-
},
230-
{
231-
name: 'run_from_block',
232-
toolId: 'run_from_block',
233-
description:
234-
'Run a workflow starting from a specific block, using cached outputs from a prior execution for upstream blocks. The workflow must have been run at least once first.',
235-
inputSchema: {
236-
type: 'object',
237-
properties: {
238-
workflowId: {
239-
type: 'string',
240-
description: 'REQUIRED. The workflow ID to run.',
241-
},
242-
startBlockId: {
243-
type: 'string',
244-
description: 'REQUIRED. The block ID to start execution from.',
245-
},
246-
executionId: {
247-
type: 'string',
248-
description:
249-
'Optional. Specific execution ID to load the snapshot from. Uses latest if omitted.',
250-
},
251-
workflow_input: {
252-
type: 'object',
253-
description: 'Optional input values for the workflow.',
254-
},
255-
useDeployedState: {
256-
type: 'boolean',
257-
description: 'When true, runs the deployed version instead of the draft. Default: false.',
258-
},
259-
},
260-
required: ['workflowId', 'startBlockId'],
261-
},
262-
annotations: { destructiveHint: false, openWorldHint: true },
263-
},
264-
{
265-
name: 'run_block',
266-
toolId: 'run_block',
267-
description:
268-
'Run a single block in isolation using cached outputs from a prior execution. Only the specified block executes — nothing upstream or downstream. The workflow must have been run at least once first.',
269-
inputSchema: {
270-
type: 'object',
271-
properties: {
272-
workflowId: {
273-
type: 'string',
274-
description: 'REQUIRED. The workflow ID.',
275-
},
276-
blockId: {
277-
type: 'string',
278-
description: 'REQUIRED. The block ID to run in isolation.',
279-
},
280-
executionId: {
281-
type: 'string',
282-
description:
283-
'Optional. Specific execution ID to load the snapshot from. Uses latest if omitted.',
284-
},
285-
workflow_input: {
286-
type: 'object',
287-
description: 'Optional input values for the workflow.',
288-
},
289-
useDeployedState: {
290-
type: 'boolean',
291-
description: 'When true, runs the deployed version instead of the draft. Default: false.',
292-
},
293-
},
294-
required: ['workflowId', 'blockId'],
295-
},
296-
annotations: { destructiveHint: false, openWorldHint: true },
297-
},
298174
{
299175
name: 'get_deployed_workflow_state',
300176
toolId: 'get_deployed_workflow_state',

apps/sim/lib/copilot/vfs/serializers.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -727,19 +727,29 @@ export function serializeIntegrationSchema(tool: ToolConfig): string {
727727
? { required: tool.oauth.required, provider: tool.oauth.provider }
728728
: undefined,
729729
params: tool.params
730-
? Object.fromEntries(
731-
Object.entries(tool.params)
732-
.filter(([key, val]) => val != null && key !== hostedApiKeyParam)
733-
.map(([key, val]) => [
734-
key,
735-
{
736-
type: val.type,
737-
required: val.required,
738-
description: val.description,
739-
default: val.default,
740-
},
741-
])
742-
)
730+
? {
731+
...Object.fromEntries(
732+
Object.entries(tool.params)
733+
.filter(([key, val]) => val != null && key !== hostedApiKeyParam)
734+
.map(([key, val]) => [
735+
key,
736+
{
737+
type: val.type,
738+
required: val.required,
739+
description: val.description,
740+
default: val.default,
741+
},
742+
])
743+
),
744+
...(tool.oauth?.required && {
745+
credentialId: {
746+
type: 'string',
747+
required: false,
748+
description:
749+
'Optional credential ID to use when multiple accounts are connected for this provider. Get IDs from environment/credentials.json. If omitted, auto-selects the first available credential.',
750+
},
751+
}),
752+
}
743753
: undefined,
744754
outputs: tool.outputs
745755
? Object.fromEntries(

apps/sim/lib/workflows/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ export async function createWorkflowRecord(params: CreateWorkflowInput) {
346346
workspaceId,
347347
name,
348348
description = null,
349-
color = '#3972F6',
349+
color = '#7F2FFF',
350350
folderId = null,
351351
} = params
352352
const workflowId = crypto.randomUUID()

apps/sim/tools/params.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ export function createUserToolSchema(toolConfig: ToolConfig): ToolSchema {
438438
}
439439
}
440440

441+
if (toolConfig.oauth?.required) {
442+
schema.properties.credentialId = {
443+
type: 'string',
444+
description:
445+
'Optional credential ID to use when multiple accounts are connected for this provider. Get IDs from environment/credentials.json. If omitted, auto-selects the first available credential.',
446+
}
447+
}
448+
441449
return schema
442450
}
443451

0 commit comments

Comments
 (0)