diff --git a/ui/desktop/src/acp/__tests__/recipeParamRequests.test.ts b/ui/desktop/src/acp/__tests__/recipeParamRequests.test.ts index f3e28a92d..41acd24c4 100644 --- a/ui/desktop/src/acp/__tests__/recipeParamRequests.test.ts +++ b/ui/desktop/src/acp/__tests__/recipeParamRequests.test.ts @@ -82,6 +82,27 @@ describe('ACP recipe param requests', () => { scope.finish(); }); + it('does not offer deeplink values for file parameters', async () => { + setRecipeParameters({ document: '/tmp/private.txt', topic: 'release notes' }); + const scope = requests.beginConfiguredRecipeParameterScope()!; + const request = recipeParamRequest('session-1', scope.id); + request.parameters.unshift({ + key: 'document', + description: 'Document', + input_type: 'file', + requirement: 'required', + }); + + const response = requests.requestAcpRecipeParams(request); + const [pendingRequest] = requests.getAcpRecipeParamRequestsSnapshot(); + + expect(pendingRequest.initialValues).toEqual({ topic: 'release notes' }); + + requests.cancelAcpRecipeParamRequest(pendingRequest.id); + await expect(response).resolves.toEqual({ action: 'cancel' }); + scope.finish(); + }); + it('allows same-session retries until a terminal response consumes the values', async () => { setRecipeParameters({ topic: 'release notes' }); const scope = requests.beginConfiguredRecipeParameterScope()!; diff --git a/ui/desktop/src/acp/recipeParamRequests.ts b/ui/desktop/src/acp/recipeParamRequests.ts index 0b9a465f2..592a0a115 100644 --- a/ui/desktop/src/acp/recipeParamRequests.ts +++ b/ui/desktop/src/acp/recipeParamRequests.ts @@ -81,8 +81,7 @@ export function beginConfiguredRecipeParameterScope(): ConfiguredRecipeParameter } const configured = window.appConfig?.get('recipeParameters') as - | Record - | undefined; + Record | undefined; if (!configured || Object.keys(configured).length === 0) { configuredParameterState = { status: 'consumed' }; return undefined; @@ -122,8 +121,15 @@ function configuredParameterValues(request: RequestRecipeParams_unstable): { if (configuredParameterState.sessionId !== request.sessionId) { return { values: {}, usesConfiguredParameters: false }; } + const fileParameterKeys = new Set( + request.parameters + .filter((parameter) => parameter.input_type === 'file') + .map((parameter) => parameter.key) + ); return { - values: { ...configuredParameterState.values }, + values: Object.fromEntries( + Object.entries(configuredParameterState.values).filter(([key]) => !fileParameterKeys.has(key)) + ), usesConfiguredParameters: true, }; } diff --git a/ui/desktop/src/components/ParameterInputModal.tsx b/ui/desktop/src/components/ParameterInputModal.tsx index 23bd753a7..fb8448f07 100644 --- a/ui/desktop/src/components/ParameterInputModal.tsx +++ b/ui/desktop/src/components/ParameterInputModal.tsx @@ -101,6 +101,10 @@ const ParameterInputModal: React.FC = ({ useEffect(() => { const values = createParameterValueMap(); parameters.forEach((param) => { + if (param.input_type === 'file') { + return; + } + if (param.requirement === 'optional' && param.default != null) { if (isValidParameterValue(param, param.default)) { values[param.key] = param.default; diff --git a/ui/desktop/src/components/__tests__/ParameterInputModal.test.tsx b/ui/desktop/src/components/__tests__/ParameterInputModal.test.tsx index 79e2f7491..97b8fc7c8 100644 --- a/ui/desktop/src/components/__tests__/ParameterInputModal.test.tsx +++ b/ui/desktop/src/components/__tests__/ParameterInputModal.test.tsx @@ -507,6 +507,44 @@ describe('ParameterInputModal', () => { expect((screen.getByLabelText(/boolean parameter/i) as HTMLSelectElement).value).toBe('true'); }); + it('does not initialize file parameters from prefills or defaults', async () => { + const user = userEvent.setup(); + const onSubmit = vi.fn(); + renderWithIntl( + + ); + + expect(screen.getByLabelText(/^Document/)).toHaveValue(''); + expect(screen.getByLabelText(/^Topic/)).toHaveValue('release notes'); + + await user.type(screen.getByLabelText(/^Document/), '/tmp/selected.txt'); + await user.click(screen.getByText('Start Recipe')); + + expect(onSubmit).toHaveBeenCalledWith({ + document: '/tmp/selected.txt', + topic: 'release notes', + }); + }); + it('keeps a valid default when an invalid prefill is supplied', () => { renderWithIntl(