fix(ai-builder): Unify post-build credential setup into single setup-workflow flow (#28273)

This commit is contained in:
Albert Alises 2026-04-10 10:06:09 +02:00 committed by GitHub
parent 733812b1a1
commit 8f8b70a301
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 19 additions and 34 deletions

View file

@ -197,8 +197,9 @@ Always pass \`conversationContext\` when spawning background agents (\`build-wor
**Post-build flow** (for direct builds via \`build-workflow-with-agent\`):
1. Builder finishes check if the workflow has mocked credentials, missing parameters, or unconfigured triggers.
2. If yes call \`setup-workflow\` with the workflowId so the user can configure them through the setup UI.
3. Ask the user if they want to test the workflow.
4. Only call \`publish-workflow\` when the user explicitly asks to publish. Never publish automatically.
3. When \`setup-workflow\` returns \`deferred: true\`, respect the user's decision — do not retry with \`setup-credentials\` or any other setup tool. The user chose to set things up later.
4. Ask the user if they want to test the workflow.
5. Only call \`publish-workflow\` when the user explicitly asks to publish. Never publish automatically.
## Tool Usage
@ -224,7 +225,7 @@ Examples: search "credential" to find setup/test/delete tools, search "file" for
}## Safety
- **Destructive operations** show a confirmation UI automatically don't ask via text.
- **Credential setup** uses \`setup-workflow\` when a workflowId is available, or \`setup-credentials\` for standalone credential creation. For builds, credentials are auto-resolved when available and auto-mocked when missing — the user is prompted to finalize through the setup UI only after verification succeeds.
- **Credential setup** uses \`setup-workflow\` when a workflowId is available — it handles credentials, parameters, and triggers in one step. Use \`setup-credentials\` only when the user explicitly asks to create a credential outside of any workflow context. Never call both tools for the same workflow.
- **Never expose credential secrets** metadata only.
- **Be concise**. Ask for clarification when intent is ambiguous.
- **Always end with a text response.** The user cannot see raw tool output. After every tool call sequence, reply with a brief summary of what you found or did even if it's just one sentence. Never end your turn silently after tool calls.

View file

@ -15,7 +15,6 @@ import { nanoid } from 'nanoid';
import { createHash } from 'node:crypto';
import { z } from 'zod';
import { createBrowserCredentialSetupTool } from './browser-credential-setup.tool';
import {
BUILDER_AGENT_PROMPT,
createSandboxBuilderAgentPrompt,
@ -43,7 +42,6 @@ import type { TriggerType, WorkflowBuildOutcome } from '../../workflow-loop';
import type { BuilderWorkspace } from '../../workspace/builder-sandbox-factory';
import { readFileViaSandbox } from '../../workspace/sandbox-fs';
import { getWorkspaceRoot } from '../../workspace/sandbox-setup';
import { createApplyWorkflowCredentialsTool } from '../workflows/apply-workflow-credentials.tool';
import { buildCredentialMap, type CredentialMap } from '../workflows/resolve-credentials';
import {
createSubmitWorkflowTool,
@ -131,13 +129,6 @@ The system tracks file hashes. If you edit the code and then call run-workflow o
- If verification fails, call debug-execution, fix the code, re-submit, and retry once
- If the same failure signature repeats, stop and explain the block
### Credential finalization
If verification succeeds with mocked credentials:
1. call setup-credentials with credentialFlow stage "finalize"
2. if it returns needsBrowserSetup=true, call browser-credential-setup then setup-credentials again
3. call apply-workflow-credentials with the workItemId and selected credentials
### Resource discovery
Before writing code that uses external services, **resolve real resource IDs**:
@ -204,7 +195,6 @@ export async function startBuildWorkflowAgentTask(
'list-workflows',
'list-credentials',
'test-credential',
'setup-credentials',
'ask-user',
'run-workflow',
'get-execution',
@ -227,10 +217,6 @@ export async function startBuildWorkflowAgentTask(
}
if (context.workflowTaskService && context.domainContext) {
builderTools['verify-built-workflow'] = createVerifyBuiltWorkflowTool(context);
builderTools['apply-workflow-credentials'] = createApplyWorkflowCredentialsTool(context);
}
if (context.browserMcpConfig) {
builderTools['browser-credential-setup'] = createBrowserCredentialSetupTool(context);
}
} else {
builderTools = {};

View file

@ -54,24 +54,23 @@ describe('formatWorkflowLoopGuidance', () => {
mockedCredentialTypes: ['slackOAuth2Api', 'gmailOAuth2'],
};
const result = formatWorkflowLoopGuidance(action);
expect(result).toContain('setup-credentials');
expect(result).toContain('slackOAuth2Api, gmailOAuth2');
expect(result).toContain('finalize');
expect(result).toContain('apply-workflow-credentials');
expect(result).toContain('setup-workflow');
expect(result).toContain('Do not call');
});
it('should use workItemId from options when mockedCredentialTypes present', () => {
it('should include workflowId in setup-workflow guidance when mockedCredentialTypes present', () => {
const action: WorkflowLoopAction = {
type: 'done',
summary: 'Done with mocks',
mockedCredentialTypes: ['notionApi'],
workflowId: 'wf-42',
};
const result = formatWorkflowLoopGuidance(action, { workItemId: 'item-42' });
expect(result).toContain('item-42');
expect(result).not.toContain('unknown');
const result = formatWorkflowLoopGuidance(action);
expect(result).toContain('setup-workflow');
expect(result).toContain('wf-42');
});
it('should default workItemId to "unknown" when not provided and mocked credentials exist', () => {
it('should default workflowId to "unknown" when not provided and mocked credentials exist', () => {
const action: WorkflowLoopAction = {
type: 'done',
summary: 'Done with mocks',
@ -251,14 +250,15 @@ describe('formatWorkflowLoopGuidance', () => {
expect(occurrences).toBeGreaterThanOrEqual(2);
});
it('should pass workItemId to done guidance with mocked credentials', () => {
it('should include workflowId in done guidance with mocked credentials', () => {
const action: WorkflowLoopAction = {
type: 'done',
summary: 'ok',
mockedCredentialTypes: ['testApi'],
workflowId: 'wf-xyz',
};
const result = formatWorkflowLoopGuidance(action, { workItemId: 'wi-xyz' });
expect(result).toContain('wi-xyz');
const result = formatWorkflowLoopGuidance(action);
expect(result).toContain('wf-xyz');
});
it('should not affect blocked or rebuild actions', () => {

View file

@ -11,13 +11,11 @@ export function formatWorkflowLoopGuidance(
switch (action.type) {
case 'done': {
if (action.mockedCredentialTypes?.length) {
const types = action.mockedCredentialTypes.join(', ');
return (
'Workflow verified successfully with temporary mock data. ' +
`Call \`setup-credentials\` with types [${types}] and ` +
'credentialFlow stage "finalize" to let the user add real credentials. ' +
'After the user selects credentials, call `apply-workflow-credentials` ' +
`with the workItemId "${options.workItemId ?? 'unknown'}" and workflowId to apply them.`
`Call \`setup-workflow\` with workflowId "${action.workflowId ?? 'unknown'}" ` +
'to let the user configure credentials, parameters, and triggers through the setup UI. ' +
'Do not call `setup-credentials` or `apply-workflow-credentials` — `setup-workflow` handles everything.'
);
}
return `Workflow verified successfully. Report completion to the user.${action.workflowId ? ` Workflow ID: ${action.workflowId}` : ''}`;