Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions app/src/components/flows/WorkflowPromptBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,25 @@ describe('WorkflowPromptBar', () => {
// The created flow is the standard blank graph (single manual trigger).
expect(graph.nodes).toHaveLength(1);
expect(graph.nodes[0].kind).toBe('trigger');
// Parity with the proposal-card path: prompt-authored flows must default
// to requiring approval, matching WorkflowProposalCard's default.
expect(requireApproval).toBe(true);
// Prompt-authored flows default to NOT requiring approval, so a
// Run-button/scheduled run doesn't deadlock on an unsurfaceable approval.
expect(requireApproval).toBe(false);
expect(navigateMock).toHaveBeenCalledWith('/flows/flow-1', {
state: { copilotBuild: { description: 'digest my Slack every morning' } },
});
});

it('defaults requireApproval to true so outbound side-effects need explicit approval', async () => {
it('defaults requireApproval to false so runs do not deadlock on an unsurfaceable approval', async () => {
render(<WorkflowPromptBar />);
fireEvent.change(screen.getByTestId('workflow-prompt-input'), {
target: { value: 'auto-reply to every gmail thread' },
});
fireEvent.click(screen.getByTestId('workflow-prompt-submit'));

await waitFor(() => expect(createFlowMock).toHaveBeenCalledTimes(1));
// Third positional arg must be `true` — flows_create's server default is
// `false`, so omitting it would silently disarm the approval gate.
expect(createFlowMock.mock.calls[0][2]).toBe(true);
// Third positional arg must be `false` — prompt-authored flows default to
// not requiring approval so a Run-button/scheduled run can execute.
expect(createFlowMock.mock.calls[0][2]).toBe(false);
});

it('submits on Enter (Shift+Enter reserved for newlines)', async () => {
Expand Down
9 changes: 5 additions & 4 deletions app/src/components/flows/WorkflowPromptBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ export default function WorkflowPromptBar({ variant = 'compact', autoFocus = fal
const name = deriveWorkflowName(trimmed, t('flows.page.newWorkflow'));
log('submit: creating flow name=%s', name);
try {
// Safe default: prompt-authored flows require approval so outbound
// Slack/Gmail/HTTP/code nodes cannot fire unattended. Omitting this
// arg would fall back to the server default of `false`.
// Prompt-authored flows default to NOT requiring approval — a
// Run-button/scheduled run has no chat context, so a parked approval
// card can't surface and the run would deadlock. The user can still
// turn approval on per-flow afterwards.
const flow = await createFlow(
name,
createBlankWorkflowGraph(name, t('flows.nodeKind.trigger')),
true
false
);
log('submit: created id=%s — opening canvas with build seed', flow.id);
navigate(`/flows/${flow.id}`, { state: { copilotBuild: { description: trimmed } } });
Expand Down
8 changes: 4 additions & 4 deletions app/src/providers/ChatRuntimeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,10 @@ function parseWorkflowProposal(output: string): WorkflowProposal | null {
return {
name: obj.name,
graph: obj.graph,
// The Rust tool defaults `require_approval` to `true` when the caller
// omits it, so treat anything other than an explicit `false` as `true`
// here too — keeps the client's fallback in lockstep with the server's.
requireApproval: obj.require_approval !== false,
// require_approval defaults to `false` — a proposal only requires approval
// if the builder explicitly set it — so treat anything other than an
// explicit `true` as `false`, in lockstep with the server default.
requireApproval: obj.require_approval === true,
summary: { trigger: typeof summary.trigger === 'string' ? summary.trigger : '', steps },
};
}
Expand Down
Loading